由于 ORDER BY 未使用索引,SQL 查询缓慢
Posted
技术标签:
【中文标题】由于 ORDER BY 未使用索引,SQL 查询缓慢【英文标题】:Slow SQL query because of ORDER BY not using index 【发布时间】:2010-12-02 14:48:00 【问题描述】:我有这个查询:
SELECT cl.title, cl.URL, cl.ID AS ad_id, cl.cat_id, cl.price, cs.name AS cat_name, pix.file_name, area.area_name
FROM classifieds cl
FORCE INDEX (advertiser_id)
INNER JOIN classifieds_pix pix ON cl.ID = pix.classified_id
INNER JOIN cat_names_sub cs ON cl.cat_id = cs.ID
INNER JOIN zip_codes zip ON cl.zip_id = zip.zip_id
INNER JOIN area_names area ON zip.area_id = area.id
WHERE cl.confirmed = 1 AND cl.price != '' AND cl.country = 'de'
GROUP BY cl.advertiser_id
ORDER BY cl.timestamp DESC
LIMIT 5
classifieds
包含 168k 行需要 > 1 秒,这太长了。 FORCE INDEX (advertiser_id)
允许我在没有 ORDER BY
子句的情况下将其降低到 0.00x 秒。 timestamp
列也已编入索引,我尝试添加 FORCE INDEX (timestamp)
,但没有帮助。
EXPLAIN
在classifieds
表中的第一个SELECT
上显示Using where; Using temporary; Using filesort
- 这显然会导致性能问题。
你能帮我解决这个问题吗?
提前致谢!
PS:本次查询的目的是获取最新的5条分类信息(包括一些附加信息,如图片、类别、邮编和地区名称)。此外,每个广告客户只应显示一个分类。有这么难吗?
PPS:我试图尽可能地确定问题并最终得到以下查询:
SELECT cl.title
FROM classifieds cl
GROUP BY cl.advertiser_id
ORDER BY cl.timestamp DESC
LIMIT 5
这需要难以置信的 23 秒!使用FORCE INDEX (advertiser_id)
,我可以将其缩短到 1 秒。如果我删除 GROUP BY 或 ORDER BY,它会下降到 0.0003 秒。
我的表/索引有问题吗?我不应该需要FORCE INDEX
(顺便说一句:USE INDEX
不起作用 - 我需要强制它!)而且它不应该花那么长时间!
【问题讨论】:
我对在 GROUP BY cl.advertiser_id 的上下文中执行 SELECT 意味着什么感到困惑。GROUP BY
确保每个广告客户只选择一个分类。
您能否发布您的查询解释计划,显示 中的索引,显示创建表 - 只需将其全部放入 pastie.org 以节省空间我们也许可以提供帮助 - tia
我已更新帖子以包含您请求的数据。
时间戳的依据是什么。就在最后一次“触摸”时,还是广告商发布的最新广告的指标?或者,是否存在与广告商的最新广告相关联的另一个“ID”键(顺序可能)。如果列出了多个广告但安排在未来的时间,如果您只关心即将到来的或在给定的时间范围内,您可能不希望这样做......可以显着帮助/影响查询。
【参考方案1】:
我认为没有任何方法可以避免对 168k 行进行排序这一事实,而不管索引如何。通过索引在表中定位行是一回事,但是一旦找到它们,引擎仍然必须对它们进行排序。
顺便说一句,1s 对我来说似乎很合理。
(删除了建议替代索引的编辑;OP 尝试了这个但没有成功)
【讨论】:
Using where; Using temporary; Using filesort
不是表示某些东西没有达到它可以/应该优化的程度吗?
@eWolf - 更正,使用文件排序并不意味着慢
我更多地指的是临时表 - 查询配置文件显示 Copying to tmp table
需要 1.205894 秒,这是迄今为止最长的部分。
@eWolf - 因为group by,删除它再试一次
-1,您在第一句话中就击败了自己的逻辑。让我反转它-'我认为有一种方法可以避免对 168M(或 k,无论如何)记录进行排序,如果存在可用于按顺序过滤和检索记录的索引'。之后你承认了复合索引的这种可能性。【参考方案2】:
虽然略有重组,但我会考虑查看分类表上的 where 子句,看看是否有任何索引可供使用...例如按确认、价格、国家/地区。无论哪个可用的可能记录计数最低,我都会首先列出——可能把国家放在第一位,然后再确认。另外,删除分组依据。您没有与查询关联的聚合函数。
SELECT STRAIGHT_JOIN
cl.title,
cl.URL,
cl.ID AS ad_id,
cl.cat_id,
cl.price,
cs.name AS cat_name,
pix.file_name,
area.area_name
FROM
( select clMax.advertiser_id,
max( clMax.TimeStamp ) as AdvMaxTime
from findix.classifieds clMax
where clMax.confirmed = 1
AND clMax.price != ''
AND clMax.country = 'de'
group by 1
order by 2 desc
limit 5 ) clQualified,
findix.classifieds cl,
findix.classifieds_pix pix,
findix.cat_names_sub cs,
findix.zip_codes zip,
findix.area_names area
WHERE
clQualified.advertiser_id = cl.advertiser_id
AND clQualified.AdvMaxTime = cl.timestamp;
AND cl.ID = pix.classified_id
AND cl.cat_id = cs.ID
AND cl.zip_id = zip.zip_id
AND zip.area_id = area.id
通过更改以匹配您的资格,我已将其移至内部预查询,该查询获取符合条件的每个广告商,获取最近的最大时间戳输入订单,并将限制为 5 作为第一个被查询的“表”为结果集。从那以后,我有 5 条记录要加入到其他表中,这在您遇到时应该几乎是瞬间完成的。
【讨论】:
+1 因为我也想知道 GROUP BY,但无法说出为什么它让我感到困扰。GROUP BY
是使每个advertisingr_id 在结果中唯一的必要条件 - 请阅读我对原始帖子所做的最新编辑以了解更多详细信息。
@eWolf,我已经根据您的反馈进行了修改...应该会有所帮助。
@eWolf,这个查询最终得到了你要找的东西吗?【参考方案3】:
你试过多索引吗?
像这样:
CREATE INDEX adv_tt ON classifieds ( advertiser_id , `timestamp` );
甚至这个:
CREATE INDEX adv_tt ON classifieds
( confirmed , price , country , advertiser_id , `timestamp` );
PS:我不知道 mysql 是先应用 GROUP BY 还是 ORDER BY,如果先应用 ORDER BY,则必须更改 INDEX 中的列顺序(...timestamp
,advertisingr_id)
【讨论】:
【参考方案4】:您是否尝试过更新表格上的统计信息?
【讨论】:
你的意思是ANALYZE TABLE
?是的,我做到了。【参考方案5】:
你的查询是 F*ed up.. 你有 GROUP BY cl.advertiser_id
但也有 ORDER BY cl.timestamp
DESC
时间戳不在Group BY
中这是不允许的!
为什么你有一个Group BY
!
把它拿出来。删除您的force index.
然后修复您的代码,使其没有任何重复,这不是 group by 的用途。
编辑: 试试
SELECT cl.title, cl.URL, cl.ID AS ad_id, cl.cat_id, cl.price, cl.timestamp
cs.name AS cat_name, pix.file_name, area.area_name
FROM findix.classifieds cl
INNER JOIN findix.classifieds_pix pix ON cl.ID = pix.classified_id
INNER JOIN findix.cat_names_sub cs ON cl.cat_id = cs.ID
INNER JOIN findix.zip_codes zip ON cl.zip_id = zip.zip_id
INNER JOIN findix.area_names area ON zip.area_id = area.id
WHERE cl.confirmed = 1 AND cl.price != '' AND cl.country = 'de'
ORDER BY cl.timestamp DESC
或
SELECT cl.advertiser_id,cl.title, cl.URL, cl.ID AS ad_id, cl.cat_id, cl.price, cl.timestamp
max(cs.name) AS cat_name, max(pix.file_name) as file_name, max(area.area_name) as area.area_name
FROM findix.classifieds cl
INNER JOIN findix.classifieds_pix pix ON cl.ID = pix.classified_id
INNER JOIN findix.cat_names_sub cs ON cl.cat_id = cs.ID
INNER JOIN findix.zip_codes zip ON cl.zip_id = zip.zip_id
INNER JOIN findix.area_names area ON zip.area_id = area.id
WHERE cl.confirmed = 1 AND cl.price != '' AND cl.country = 'de'
Group By cl.advertiser_id,cl.title, cl.URL, cl.ID AS ad_id, cl.cat_id, cl.price, cl.timestamp
ORDER BY cl.timestamp DESC
【讨论】:
GROUP BY
应该只为每个广告客户选择一个分类 - 这是错误的做法吗?
Id 子表中有 4 个不同的值,查询如何知道取哪一个? (见我的编辑)。
您的第一个查询运行速度很慢(0.7 秒)。解释说Using temporary; Using filesort
。第二个需要 1 秒,也使用临时表和文件排序。
查看我原始帖子的最新编辑。我高度怀疑这与我的索引/表有关。以上是关于由于 ORDER BY 未使用索引,SQL 查询缓慢的主要内容,如果未能解决你的问题,请参考以下文章