我正在尝试优化以下查询:
SELECT name
FROM tbl
WHERE user_id
IN (".$user_ids.")
GROUP BY name ORDER BY SUM(counter) DESC LIMIT 10
Tbl info:name是VARCHAR,counter和user_id是INT。 user_id,名称是唯一的。
我尝试添加IDX(user_id, counter, name)
,但在EXPLAIN
我仍然看到Using where; Using index; Using temporary; Using filesort
所以我猜我做错了。
此类查询的正确索引是什么?
答案 0 :(得分:1)
以下内容可能会改善您的表现:
select t.name,
(select sum(counter) from tbl t2 where t2.name = t.name) as sumcounter
from (select distinct name
from tbl
where user_id IN (".$user_ids.")
) t
order by sumcounter desc;
现在将索引放在tbl(user_id, name)
和tbl(name, counter)
上。
如果这样可行,那是因为内部子查询使用第一个索引来获取不同的名称。 select
中的嵌套子查询将使用第二个索引来计算计数。
我不喜欢重写这样的查询。有时可能需要获得所需的性能。
答案 1 :(得分:1)
正确的索引是IDX(user_id, name, counter)
,但在从索引中获取数据后,查询需要额外的计算。如果不同名称的数量大约是10,那么你几乎无法做任何事情(大部分时间是由sum运算完成的),但是如果有许多不同的名称,你可以通过使用关于{的一些经验知识来减少排序。 {1}}门槛:
SUM(counter)
UPD1。嗯,如果你说你已经尝试了SELECT name
FROM tbl
WHERE user_id IN (".$user_ids.")
GROUP BY name
HAVING SUM(counter) > 1000 -- adjust the threshold
ORDER BY SUM(counter) DESC LIMIT 10
索引并且性能相同,我实际上看不出它为什么慢的原因,除非你通过几百个用户id(在这种情况下时间是用于查询解析而不用于执行)。
UPD2。 MySQL IN运算符会做一些额外的魔术:
如果expr等于IN列表中的任何值,则返回1,否则返回0.如果所有值都是常量,则根据expr的类型计算它们并进行排序。然后使用二分搜索完成对项目的搜索。
这意味着如果将INT值传递给运算符IDX(user_id, name, counter)
,它们将被排序为INTS,如果序列化存储为字符串IN (1,2,3)
的整数,则它们按字典顺序排序。排序的基本原理是消除随机索引读取,这在将大量值传递给运算符时非常重要。