由于 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),但没有帮助。

EXPLAINclassifieds 表中的第一个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 查询缓慢的主要内容,如果未能解决你的问题,请参考以下文章

ORDER BY导致索引使用不理想

ORDER BY导致索引使用不理想

MySQL的order by该如何避免“未命中索引“

MySQL的order by该如何避免“未命中索引“

MySQL的order by该如何避免“未命中索引“

MySQL的order by该如何避免“未命中索引“