【mysql】为什么count那么慢?

最近有个接口偶尔超时,定位发现某些user因为记录数较多,导致count的SQL耗费了1~2秒,分析到最后其实就回到了老问题:“覆盖索引”。

背景

表结构如下:

慢SQL如下,统计出的count值为2万,耗时1.8秒:

数据库是SSD磁盘,性能方面没有问题。

分析

SQL走idx_user_id_article_id索引过滤user_id=383822的记录,一共就2万多条,再怎么count也不应该卡顿到2秒时间吧?

count(distinct article_id) 是问题吗?当然也不是,2万条article_id去重没什么大不了的。

那么问题可能是article_channel_id!=22这个条件吧?

没错,去掉这个条件后SQL就不再卡顿了。

我们回顾一下innodb的索引,分为:

  • Primary Key的主索引
  • 其他辅助索引

当我们走idx_user_id_article_id辅助索引的时候,可以根据user_id快速定位到该用户的2万条记录,但是为了进一步过滤article_channel_id条件,必须回到主索引上来查询该字段的值。

我们知道B+树相同的key会紧密排列在相同的磁盘页上,顺序读性能是很高的,因此辅助索引在这里效率非常高;但是当拿着2万条记录的id回到主索引查询article_channel_id字段的时候,这2万条id在B+树上的分布就是散乱的,所以可能要从磁盘上的不同位置读入多个磁盘页,这就花费了很多时间做I/O。

优化

我们只需要让辅助索引的B+树key中包含article_channel_id,这样过滤时就不需要回到Primary Key的索引做二次读取了。

修改索引:KEY idx_user_id_article_id (user_id,article_id, article_channel_id`),加入这一列即可,虽然B+树定位的时候用不上,但是过滤的时候就不需要回主索引了。

如果文章帮助您解决了工作难题,您可以帮我点击屏幕上的任意广告,或者赞助少量费用来支持我的持续创作,谢谢~