让我的查询更短,更快,更好

时间:2017-11-19 16:50:09

标签: php mysql query-optimization

我正在运行一个大型PHP脚本来获取适用于搜索查询等的数据库信息。问题是页面的加载时间是几秒钟(本地),而远程数据库的加载时间是一分钟。

在优化方面,我不是最好的,但我知道查询中的星号,但在这种情况下我需要它,因为我正在使用表中的所有字段。

请记住,它从中获取数据的表总共超过800k行(没有显示800k行,如我在显示它的代码WHERE = something中所示)

<?php

    $getPage = $mysqli->real_escape_string(@$_GET["page"]); 
    $getSearch = $mysqli->real_escape_string(@$_GET["search"]);
    $getUser = $mysqli->real_escape_string(@$_GET["user"]);

    if(isset($getPage)) { $page  = $getPage; } else { $page = 1; }; 
    $start_from = ($page-1) * 12;

    $order = $mysqli->real_escape_string(@$_GET["ord"]); 

    $order_query = "item_name ASC";

    if($start_from >= 0) {
        if($getSearch)
        {
            if($order)
            {
                if($order == 1)
                {
                    $order_query = "item_name ASC";
                }

                if($order == 2)
                {
                    $order_query = "item_name DESC";
                }

                if($order == 3)
                {
                    $order_query = "item_id ASC";
                }

                if($order == 4)
                {
                    $order_query = "item_id DESC";
                }

                $shop = $mysqli->query("SELECT * FROM store_inventory WHERE (item_name LIKE '%".$getSearch."%' OR item_type LIKE '%".$getSearch."%' OR item_quality LIKE '%".$getSearch."%')  AND steamid = '".$getUser."' GROUP BY item_id ORDER BY ".$order_query." LIMIT $start_from, 28");
            }
            else
            {
                $shop = $mysqli->query("SELECT * FROM store_inventory WHERE (item_name LIKE '%".$getSearch."%' OR item_type LIKE '%".$getSearch."%' OR item_quality LIKE '%".$getSearch."%')  AND steamid = '".$getUser."' GROUP BY item_id ORDER BY item_name ASC LIMIT $start_from, 28");
            }
        }
        else 
        {
            if($order)
            {
                if($order == 1)
                {
                    $order_query = "item_name ASC";
                }

                if($order == 2)
                {
                    $order_query = "item_name DESC";
                }

                if($order == 3)
                {
                    $order_query = "item_id ASC";
                }

                if($order == 4)
                {
                    $order_query = "item_id DESC";
                }

                $shop = $mysqli->query("SELECT * FROM store_inventory WHERE steamid = '".$getUser."' GROUP BY item_id ORDER BY ".$order_query." LIMIT $start_from, 28");
            }
            else
            {
                $shop = $mysqli->query("SELECT * FROM store_inventory WHERE steamid = '".$getUser."' GROUP BY item_id ORDER BY item_name ASC LIMIT $start_from, 28");
            }
        }
    } else {
        $start_from = 0;
        if($getSearch)
        {
            if($order)
            {
                if($order == 1)
                {
                    $order_query = "item_name ASC";
                }

                if($order == 2)
                {
                    $order_query = "item_name DESC";
                }

                if($order == 3)
                {
                    $order_query = "item_id ASC";
                }

                if($order == 4)
                {
                    $order_query = "item_id DESC";
                }

                $shop = $mysqli->query("SELECT * FROM store_inventory WHERE (item_name LIKE '%".$getSearch."%' OR item_type LIKE '%".$getSearch."%' OR item_quality LIKE '%".$getSearch."%')  AND steamid = '".$getUser."' GROUP BY item_id ORDER BY ".$order_query." LIMIT $start_from, 28");
            }
            else
            {
                $shop = $mysqli->query("SELECT * FROM store_inventory WHERE (item_name LIKE '%".$getSearch."%' OR item_type LIKE '%".$getSearch."%' OR item_quality LIKE '%".$getSearch."%')  AND steamid = '".$getUser."' GROUP BY item_id ORDER BY item_name ASC LIMIT $start_from, 28");
            }
        }
        else 
        {
            if($order)
            {
                if($order == 1)
                {
                    $order_query = "item_name ASC";
                }

                if($order == 2)
                {
                    $order_query = "item_name DESC";
                }

                if($order == 3)
                {
                    $order_query = "item_id ASC";
                }

                if($order == 4)
                {
                    $order_query = "item_id DESC";
                }

                $shop = $mysqli->query("SELECT * FROM store_inventory WHERE  steamid = '".$getUser."' GROUP BY item_id ORDER BY ".$order_query." LIMIT $start_from, 28");
            }
            else
            {
                $shop = $mysqli->query("SELECT * FROM store_inventory WHERE  steamid = '".$getUser."' GROUP BY item_id ORDER BY item_name ASC LIMIT $start_from, 28");
            }
        }
    }

    $countarticles = $mysqli->query("SELECT COUNT(item_id) FROM store_inventory");
    $row = $countarticles->fetch_row();
    $total_records = $row[0]; 
    $total_pages = ceil($total_records / 28);

    $Pages[] = "";        
    for ($i = 1; $i <= $total_pages; $i++) { 
        $Pages[] = "<li><a href='?page=".$i."'>".$i."</a></li>";
    }

    while($fetch_market = $shop->fetch_array())
    {
        $iInv[] = $fetch_market;
    }

如何显着减少脚本/页面的加载时间?

2 个答案:

答案 0 :(得分:1)

这是您的选择查询。

SELECT * 
  FROM store_inventory
 WHERE (   item_name LIKE '%".$getSearch."%'
        OR item_type LIKE '%".$getSearch."%'
        OR item_quality LIKE '%".$getSearch."%'
       )
   AND steamid = '".$getUser."'
 GROUP BY item_id ORDER BY ".$order_query."  
 LIMIT $start_from, 28

相对而言,您编写此查询的方式保证非常慢。

为什么?

首先,您正在使用WHERE column LIKE '%value%'。这种过滤器表达式不可能被索引加速。为什么不?因为它必须查看列中的每个值以查看它是否与您的表达式匹配。另一方面,WHERE column LIKE 'value%'没有前导%,可以利用索引。您拥有它的方式需要全表扫描才能执行过滤。

其次,您使用了三个OR个表达式,每个表达式都有效果不佳的LIKE过滤器。这会将您的一张桌子扫描成三张,将所用时间增加三倍。

第三,你在这里滥用GROUP BY。 MySQL可以让你在这里找到各种奇怪的东西。阅读本文:https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html我会就如何解决此问题提出建议,但我无法猜测你想要做什么。

第四,您正在使用臭名昭着的SELECT * ... ORDER BY x LIMIT y性能反模式。这样排除了一大堆乱七八糟的行,只是为了丢弃其中的几行。

你能做些什么?首先,确保您在steamid列上有索引。如果它具有合理的选择性,那么通过减少需要扫描的行数会有很大帮助。

其次,尝试用

上的复合索引替换steamid索引
steamid, item_name, item_type, item_quality

可能(或可能不会)使您的LIKE扫描效率更高。

第三,找出你用GROUP BY实际想要完成的事情并尝试正确地做到这一点。

第四,尽量减轻ORDER BY的负担。这样的事情可能有所帮助。

SELECT * FROM store_inventory
 WHERE id IN (SELECT id
                FROM store_inventory
               WHERE (    this
                       OR that
                       OR the_other_thing
                     )
                 AND steamid = something
               ORDER BY some_column
               LIMIT start, count
              )

这使得它只需要对id进行排序。它更快。

第五,查看MySQL的FULLTEXT搜索选项。它可以用更高效的方式替换你的LIKE系列操作。

答案 1 :(得分:0)

使用PHP 7,准备好的语句和函数!

为什么要使用PHP 7? 根据[Anna Monus] [1]

,PHP 7比PHP 5.6快两倍

为什么要使用预备声明? PHP7 + MySQLi提供更好的性能和安全性。但是,您的代码仍然容易受到SQL注入的攻击。请注意,准备好的语句可能很慢。 准备好的语句只有在您准备语句然后多次执行时才会变得更快。但是,因为您正在使用mysqli_real_escape_string,它会对数据库进行往返,通过替换所有使用单个预准备语句的那些往返使您的代码更快。

为什么要使用功能 函数使您的代码更短,更易于维护,并且在大多数情况下,执行速度更快。因为您不断重复您的代码。您可以使用功能缩短它。

我知道这已经够了,所以这是你的代码:

$getPage = $mysqli->real_escape_string(@$_GET["page"]); 
$getSearch = $mysqli->real_escape_string(@$_GET["search"]);
$getUser = $mysqli->real_escape_string(@$_GET["user"]);

if(isset($getPage)) { $page  = $getPage; } else { $page = 1; }; 
$start_from = ($page-1) * 12;

$order = $mysqli->real_escape_string(@$_GET["ord"]); 

$order_query = "item_name ASC";

function order_item($order) {
    if($order == 1) {
        $order_query = "item_name ASC";
    }
    if($order == 2) {
        $order_query = "item_name DESC";
    }
    if($order == 3) {
        $order_query = "item_id ASC";
    }
    if($order == 4) {
        $order_query = "item_id DESC";
    }
return $order_query;
}

function shop_type($type, $orderBy) {
    $getSearch = "%$getSearch%";
    if ( $type = 'searched' ) {
        $add = "AND (item_name LIKE ? OR item_type LIKE ? OR item_quality LIKE ?)";
    } else {
        $add = "";
    }
    $stmt = $con->prepare("SELECT * FROM store_inventory 
        WHERE steamid = ? 
        ".$add."
        GROUP BY item_id
        ORDER BY ".$orderBy." ASC
        LIMIT $start_from, 28"); 
    $stmt->bind_param("is", $getUser, $getSearch);
    $stmt->execute();
    $result = $stmt->get_result();
    if($result->num_rows === 0) exit('No rows');
    while($row = $result->fetch_assoc()) {
      $arr[] = $row;
    }
    return $arr;
}

function get_items() {
     if($getSearch) {
        if($order) {
            order_item($order);
            $shop = shop_type('searched',$order_query);
        } else {
            $shop = shop_type('searched','item_name');
        }
    } else {
        if($order) {
            order_item($order);
            $shop = shop_type('unsearched',$order_query);
        } else {
            $shop = shop_type('unsearched','item_name');
        }
    }
}

if($start_from >= 0) {
   get_items();
} else {
    $start_from = 0;
    get_items();
}

$countarticles = $mysqli->query("SELECT COUNT(item_id) FROM store_inventory");
$row = $countarticles->fetch_row();
$total_records = $row[0]; 
$total_pages = ceil($total_records / 28);

$Pages[] = "";        
for ($i = 1; $i <= $total_pages; $i++) { 
    $Pages[] = "<li><a href='?page=".$i."'>".$i."</a></li>";
}

while($fetch_market = $shop->fetch_array())
{
    $iInv[] = $fetch_market;
}

另一个提示:

将函数放在单独的PHP文件中,并使用:

调用它
require "filename.php";

希望这有帮助!