让我的查询更短、更快、更好
Posted
技术标签:
【中文标题】让我的查询更短、更快、更好【英文标题】:Making my queries shorter, faster and better 【发布时间】:2017-11-19 16:50:09 【问题描述】:我正在运行一个大型 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;
如何显着减少脚本/页面的加载时间?
【问题讨论】:
首先避免使用 select * 直到并且除非你需要整行 @DhavalChheda 正如我所说,我需要整行。 你有关于 steamid 的索引吗? 不,steamid 上没有索引 @P.Nick 当您请求查询优化帮助时,请在查询中为每个表添加SHOW CREATE TABLE
,这样我们就不必猜测数据类型、索引和约束。帮助我们帮助您!
【参考方案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
)
这使得 MySQL 只需对 id 进行排序。它更快。
第五,查找 MySQL 的 FULLTEXT 搜索选项。它可能会用更有效的方法取代您的一系列LIKE
操作。
【讨论】:
感谢您的意见!调查一下!【参考方案2】:使用 PHP 7,准备好的语句和函数!
为什么使用 PHP 7? 根据 [Anna Monus][1]
,PHP 7 比 PHP 5.6 快两倍为什么要使用准备好的语句? PHP7 + MySQLi 提供更好的性能和安全性。但是,您的代码仍然容易受到 SQL 注入的影响。请注意,Prepared Statements 可能很慢。 只有在准备语句然后多次执行时,准备好的语句才会变得更快。 但是由于您使用的是 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";
希望这会有所帮助!
【讨论】:
恕我直言,这是个好建议,但对问题没有回应。 很遗憾听到这个消息。但就我而言,我让他的代码 (1) 更短 (2) 更快,并希望 (3) 更好。 :D 有时会更快,有时会慢 10 倍。不知道是什么原因造成的。 我忘了说 LIKE 查询很慢。尝试使用 O. Jones 建议的其他查询。还可以尝试使用 microtime 检查执行速度;这可能会查明有问题的函数/查询,以便我们为您提供更好的答案。以上是关于让我的查询更短、更快、更好的主要内容,如果未能解决你的问题,请参考以下文章
我正在寻找一种更短/更好的方法来从我的核心数据应用程序中的 NSSet 获取第一个对象
MongoDB:运行更快的查询,这更好 Pymongo 或 MongoEngine