mysql-简单查询优化(WHERE ORDER LIMIT)
作者:互联网
我有这个查询,运行速度令人难以置信(4分钟):
SELECT * FROM `ad` WHERE `ad`.`user_id` = USER_ID ORDER BY `ad`.`id` desc LIMIT 20;
广告表大约有1000万行.
SELECT COUNT(*) FROM `ad` WHERE `ad`.`user_id` = USER_ID;
返回1万行.
表具有以下索引:
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_user_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分钟):
SELECT *
FROM `ad` USE INDEX (idx_user_id)
WHERE `ad`.`user_id` = 1884774
ORDER BY `ad`.`id` desc LIMIT 20;
解决方法:
在EXPLAIN输出中,您可以看到键值是PRIMARY.这意味着MySQL优化程序决定扫描所有表记录(已按ID排序)并使用特定的user_id值搜索前20条记录要比使用idx_user_id键更快,后者被优化程序视为可能的键,然后再使用被拒绝.
在第二个查询中,优化器发现子查询中仅需要id值,并决定改用idx_user_id索引,因为该索引允许计算所需ID的列表,而无需接触表本身.然后,通过直接搜索主键值仅检索20条记录,这对于少数记录是非常快速的操作.
当您使用ANOTHER_ID显示查询时,MySQL错误决定是基于先前USER_ID值的行数.这个数字是如此之大,以至于优化器猜测它仅通过查看表记录本身并跳过具有错误的user_id值的记录就能更快地找到具有该特定user_id的前20条记录.
如果通过索引访问表行,则需要随机访问操作.对于典型的HDD随机访问操作,其速度比顺序扫描慢大约100倍.因此,为了使索引有用,必须将行数减少到少于总行数的1%.如果特定USER_ID值的行占行总数的1%以上,则如果要检索所有这些行,则执行全表扫描而不是使用索引可能会更有效.但是MySQL优化器没有考虑到只有20行会被检索的事实.因此,它错误地决定不使用索引,而是进行全表扫描.
为了快速查询任何user_id值,您可以再添加一个索引,这将允许以最快的方式执行查询:
create index idx_user_id_2 on ad(user_id, id);
该索引允许MySQL进行过滤和排序.为此,用于过滤的列应放在第一位,用于排序的列应放在第二位. MySQL应该足够聪明才能使用该索引,因为该索引允许搜索所有必要的记录而不会跳过任何记录.
标签:query-optimization,sql,mysql 来源: https://codeday.me/bug/20191121/2048036.html