Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/69.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Mysql 简单查询优化(WHERE+;ORDER+;LIMIT)_Mysql_Sql_Query Optimization - Fatal编程技术网

Mysql 简单查询优化(WHERE+;ORDER+;LIMIT)

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

我的这个查询运行速度慢得令人难以置信(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: 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);