vlambda博客
学习文章列表

MySQL 我们来聊聊 count(*) 语句的优化方式?

在实际业务开发中,可能会经常需要计算一个表的行数,于是你可能会使用的查询语句

 select count(*) from question_extracting

如下图所示:

MySQL 我们来聊聊 count(*) 语句的优化方式?



上述查询这个数量尽然花了640 ms ,我们不妨再添加个筛选条件

SELECT
count(*)
FROM
question_extracting
WHERE
create_time BETWEEN '2019-01-05'
AND '2019-06-05'

MySQL 我们来聊聊 count(*) 语句的优化方式?


尽然花了1.6 秒,这是不可忍的,你知道发生了什么吗 ?


1 MySQL count(*) 的实现原理

一句话描述就是 InnoDB 引擎在执行 count(*) 的时候,上把数据一行一行地从引擎里面读出来,然后累积计数。

InnoDB 的事务设计中,可重复读是它默认的隔离级别,InnoDB在计算表中记录数据时,每一行记录都要判断这一条记录是否对本次的查询可见,只有可见的行才能够用于计算“基于这个查询”的表的总行数。

所在每次查询一个表的相关计数,如果都使用 MySQL count(*) 来查询表的总行数,性能上会有很大的问题。

2 将计数保存在缓存系统中 可以算是一个优化方式

Redis 是一个不错的选择,可以用一个 Redis 服务来保存表的总行数,然后表每被插入一行 Redis 计数就加 1,每被删除一行 Redis 计数就减 1。

但是当 Redis 异常重启以后,你的数据就有可能丢失,所以解决方式就是 当 Redis 异常重启以后 ,再到数据库里面单独执行一次 count(*) 获取真实的行数保存。

在并发的情况下,这种缓存的计算方式就极度不精确了,原因是:如查询的是 200行

  • 查到的 200 行结果里面有最新插入记录,而 Redis 的计数里还没加 1

  • 查到的 200 行结果里没有最新插入的记录,而 Redis 的计数里已经加了 1

3 将计数保存在数据库中是一个不错的选择

用缓存系统保存计数有丢失数据和计数不精确的问题,所以我们可以考虑将计数结果保存在单独的表中,就可以解决计数的问题了。


4 count(主键 id) ?

在 InnoDB 引擎中,我们可以通过 count(*)、count(主键 id)、count(字段) 和 count(1) 等不同的方式来查询计数。

count() 是一个聚合函数,对于返回的结果集,一 行行地判断进行计算。

count(*)、count(主键 id) 和 count(1) 都表示返回满足条件的结果集的总行数。

count(字段),则表示返回满足条件(参数“字段”不为 NULL 的)的数据行的总个数。


4.1 count(主键 id)

InnoDB 引擎会遍历整张表,把每一行的 id 值都取出来,返回给 server 层,然后在server 层中判断按行累加。

4.2 count(1)

InnoDB 引擎遍历整张表,但不取值,server 层对于返回的每一行, 放一个数字“1”进去,按行累加,所以 count(1) 要比 count(主键 id) 快。

4.3 count(字段)

如果这个“字段”是定义为 not null,那么InnoDB 引擎会一行行地从记录里面读出这个字段,server 层判断不能为 null,按行累加;

如果这个“字段”定义允许为 null,那么InnoDB 引擎会一行行地从记录里面读出这个字段,然后把值取出来再判断一下,不是 null 才累加。

4.4 count(*)

MySQL 对 count(*)进行了优化, count(*)直接扫描主键索引记录,并不会把全部字段取出来,直接按行累加。

按照效率排序的话,count(字段)<count(主键 id)<count(1)≈count(*)


完毕

不局限于思维,不局限语言限制,才是编程的最高境界。






来源:

https://www.toutiao.com/i6939009398236873219/

“IT大咖说”欢迎广大技术人员投稿,投稿邮箱:[email protected]



来都来了,走啥走,留个言呗~




 IT大咖说  |  关于版权 

感谢您对IT大咖说的热心支持!





相关推荐


推荐文章