Mysql 简单查询优化(WHERE+;ORDER+;LIMIT)
我的这个查询运行速度慢得令人难以置信(4分钟): Ad表大约有1000万行Mysql 简单查询优化(WHERE+;ORDER+;LIMIT),mysql,sql,query-optimization,Mysql,Sql,Query Optimization,我的这个查询运行速度慢得令人难以置信(4分钟): Ad表大约有1000万行 SELECT COUNT(*) FROM `ad` WHERE `ad`.`user_id` = USER_ID; 返回10k行 表具有以下索引: PRIMARY KEY (`id`), KEY `idx_user_id` (`user_id`,`status`,`sorttime`), 解释一下: id: 1 select_type: SIMPLE table: a
SELECT COUNT(*) FROM `ad` WHERE `ad`.`user_id` = USER_ID;
返回10k行
表具有以下索引:
PRIMARY KEY (`id`),
KEY `idx_user_id` (`user_id`,`status`,`sorttime`),
解释一下:
id: 1
select_type: SIMPLE
table: ad
type: index
possible_keys: idx_user_id
key: PRIMARY
key_len: 4
ref: NULL
rows: 4249
Extra: Using where
我不明白为什么要花这么长时间?此外,该查询是由ORM(分页)生成的,因此最好从外部对其进行优化(可能会添加一些额外的索引)
顺便说一句,这个查询工作得很快:
select aa.*
from (select id from ad where user_id=USER_ID order by id desc limit 20) as a
join ad as aa on a.id = aa.id ;
编辑:我尝试了另一个用户,其行数(几十行)比原始用户少很多。我想知道为什么原始查询不使用idx\u user\u id
:
EXPLAIN SELECT * FROM `ad` WHERE `ad`.`user_id` = ANOTHER_ID ORDER BY `ad`.`id` desc LIMIT 20;
id: 1
select_type: SIMPLE
table: ad
type: ref
possible_keys: idx_user_id
**key: idx_user_id**
key_len: 3
ref: const
rows: 84
Extra: Using where; Using filesort
Edit2:在Alexander的帮助下,我决定尝试强制MySQL使用我想要的索引,下面的查询要快得多(1秒而不是4分钟):
在
解释
输出中,您可以看到键
值是主
。这意味着MySQL优化器决定扫描所有表记录(已按id
排序)并搜索前20条具有特定user\u id
值的记录要比使用idx\u user\u id
键更快,优化器认为这是一个可能的键,然后拒绝
在第二个查询中,优化器发现子查询中只需要id
值,并决定改用idx\u user\u id
索引,因为该索引允许计算必要的id
列表,而无需触摸表本身。然后通过主键值直接搜索只检索到20条记录,这对于少量记录来说是非常快速的操作
当您使用另一个\u ID
查询时,MySQL错误的决定是基于上一个用户\u ID
值的行数。这个数字如此之大,以至于优化器猜测,只要查看表记录本身并跳过具有错误用户id
值的记录,就可以更快地找到具有此特定用户id
的前20条记录
如果表行是通过索引访问的,则需要随机访问操作。对于典型的HDD,随机存取操作比顺序扫描慢大约100倍。所以为了使索引有用,它必须将行数减少到总行数的1%以下。如果特定USER\u ID
值的行占总行数的1%以上,如果我们要检索所有这些行,则执行完整表扫描而不是使用索引可能更有效。但是MySQL优化器没有考虑到这样一个事实,即只有20行将被检索。所以,它错误地决定不使用索引,而是进行全表扫描
为了快速查询任何用户id
值,您可以再添加一个索引,以便以最快的方式执行查询:
create index idx_user_id_2 on ad(user_id, id);
这个索引允许MySQL进行过滤和排序。为此,用于筛选的列应放在第一位,用于排序的列应放在第二位。MySQL应该足够聪明,可以使用该索引,因为该索引允许搜索所有必要的记录,而不会跳过任何记录。如果从第一个查询中删除order by,它执行得快吗?@GreenChili它非常快啊,我没有注意到键列中的PRIMARY。我真的不能在生产上玩,在开发上创建这样的索引需要很多时间。你能看看我的编辑吗?MySql绝对可以在这样的查询中使用复合索引,它只是做出了一个我不理解的判断。因为这是非常次要的事情,所以我不会创建索引并尝试在查询中使用提示(如果我知道如何将其注入ORM)。但是你说的很有道理。考虑到WHERE只匹配1%的记录,Mysql决定跳过PK会更快,这很奇怪。那很好,你能接受我的答案吗?;)索引访问通常比表扫描慢100倍,因为索引需要随机访问,而对于典型的HDD,它非常慢。因此,只有当允许将记录计数限制在总计数的1%以下时,索引才是好的。
SELECT *
FROM `ad` USE INDEX (idx_user_id)
WHERE `ad`.`user_id` = 1884774
ORDER BY `ad`.`id` desc LIMIT 20;
create index idx_user_id_2 on ad(user_id, id);