数据库
首页 > 数据库> > mysql-简单查询优化(WHERE ORDER LIMIT)

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