count(*)这么慢,我该怎么办?

1)计算一个表有多少行数用什么命令?

  • select count(*) from t

2)count(*)底层是怎样实现的?

  • 在MYISAM中,是把这个总行数存到磁盘中去的,要的时候直接去读就行,特别快。

  • 而在InnoDB引擎中,这个总数是需要去一行一行的扫描表,然后累加起来看有多少行。所以当数据量大之后,count(*)就变慢了。

3)为什么InnoDB引擎不像MYISAM一样把总数直接存到磁盘中?

  • 总数是不断变化的,并且InnoDB还有MVCC多版本并发控制,每一行都要判断对当前这个会话是否可见,每个会话下面读到的值可能都不一样。变数比较大,所以不能写死 。

4)针对InnoDB这种引擎,要想快速读取count(*),有什么优化方案吗?

  • 可以考虑用redis来存总数,插入一行总数+1,删除一行-1。但是用redis的话 不能保证数据的一致性,有可能有些计数没有保存但是redis挂掉了。

  • 针对redis挂掉会造成数据一致性问题我们考虑直接将这个总数放在数据库某张表中存起来,这样即使数据库挂了,重启之后也是能恢复数据的。

5)MYSQL对count(*)一点优化都没有吗 ?

  • 还是有点优化的,我们知道主键索引存的是整行记录,非主键索引存的是主键值。那非主键索引生成的这棵树是远远要小于主键索引这棵树的。我count(*)走主键索引查和走非主键索引查,得到的结果都是一样的,那我当然走树小的非主键索引这棵。

6)有一个命令是show table state,他查出来的里面有个TABLE_ROWS值,那我们可不可以用这个命令来代替count(*)呢?

  • 当然不可以,show table state他算总数的话是估算,误差是比较大的,一半的误差左右。

7)MYISAM虽然查总数快,但不支持事务,show table state呢又不精准,count(*)呢又会有性能问题,那现在我有一个页面要经常显示总数,那我们该怎么办?

  • 自己计数。找一个地方,把总数存起来。就是我们4)中的两个方案。

8)count(*),count(1),count(字段),count(主键id)有什么区别?

下面的引擎都指的是InnoDB

  • count(1) :引擎扫描全表,但不取值,直接全部丢给server层去。server 层对于返回的每一行,放一个数字“1”进去,判断是不可能为空的,加起来的结果就是总数。

  • count(主键 id):引擎扫描全表 ,把id值取出来,返回给server层。到了server层之后,server层把id不为空的加起来就是结果。

  • count(字段):引擎扫描,一行一行取出来,然后拿给server层。

    • 如果这个字段定位为not null:那server层直接数有多少行

    • 如果这个字段可以为null:server层看返回的这些行,字段不为空的那就累加起来返回。

  • count(*):MYSQL有优化,确保行中肯定不为null,所以直接数有多少行,这个速度还是挺快的,所以我们首选还是得这个。 

posted on 2022-03-24 15:11  Love&Share  阅读(554)  评论(0编辑  收藏  举报

导航