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

一、抛砖引玉
MySQL的count(*) 语句到底是怎样实现的,以及 MySQL 为什么会这么实现?

二、正文开始

🏁:1.这个问题回答之前需要从不同的数据库引擎分析

  • MyISAM 引擎把一个表的总行数存在了磁盘上,因此执行 count(*) 的时候会直接返回这个数,效率很高;
  • InnoDB 引擎它执行 count(*) 的时候,需要把数据一行一行地从引擎里面读出来,然后累积计数。

⚠️⚠️⚠️:你要是加了where条件MyISAM也懵逼,不会返回这么快

👋:2.为什么Innodb不把数字事先存起来呢?
因为即使是在同一个时刻的多个查询,由于多版本并发控制(MVCC)的原因,InnoDB 表“应该返回多少行”也是不确定的。
举例:
在这里插入图片描述

show table status 命令的话,就会发现这个命令的输出结果里面也有一个 TABLE_ROWS 用于显示这个表当前有多少行,这个命令执行挺快的,那这个 TABLE_ROWS 能代替 count(*) 吗?
不能!!! 因为索引统计的值是通过采样来估算的,不准的概率达到40-50%

🏁:3.这时候如果用缓存计数会有什么问题?不用缓存还有什么方案可以替代。
用缓存系统保存计数有丢失数据和计数不精确的问题。那么,如果我们把这个计数直接放到数据库里单独的一张计数表 C 中,又会怎么样呢?
在这里插入图片描述
🏁:4.面试题来了!!!
count(*)、count(主键 id)、count(字段) 和 count(1) 等不同用法的性能,有哪些差别?

⭐️:非性能差别。
4.1.count(*)、count(主键 id) 和 count(1) 都表示返回满足条件的结果集的总行数;而 count(字段),则表示返回满足条件的数据行里面,参数“字段”不为 NULL 的总个数

  • count(id)。InnoDB 引擎会遍历整张表,把每一行的 id 值都取出来,返回给 server 层。server 层拿到 id 后,判断是不可能为空的,就按行累加。

  • count(1) 。InnoDB 引擎遍历整张表,但不取值。server 层对于返回的每一行,放一个数字“1”进去,判断是不可能为空的,按行累加。

    🚩:count(1) 要比count(id)要快,因为从引擎返回 id 会涉及到解析数据行,以及拷贝字段值的操作。

  • 对于 count(字段) 来说分为两种情况:这个字段为not null还是被定义为了null,定义为null时,还需要把值取出来在判断一下不是null才累加。
    ⚠️⚠️⚠️:这里没有分析字段上有没有索引,**如果字段上没有加索引。count(字段)会比count(id)慢!**因为,count(id)可能会选择最小的索引来遍历,而count(字段)的话,如果字段上没有索引,就只能选主键索引

  • 但是 count() 是例外,并不会把全部字段取出来,而是专门做了优化,不取值。count() 肯定不是 null,按行累加。

所以结论是:
count(字段):遍历整张表,需要取值,判断 字段 != null,按行累加;
count(id) :遍历整张表,需要取ID,判断 id !=null,按行累加;
count(1) : 遍历整张表,【不需要】取值,返回的每一行放一个数字1,按行累加;
count() : 【不需要取字段】,count(),按行累加; 因为count(*) 和 count(1) 不取字段值,减少往 server层的数据返回,所以比其他count(字段)要返回值的【性能】较好;

三、思考题
由于事务可以保证中间结果不被别的事务读到,因此修改计数值和插入新记录的顺序是不影响逻辑结果的。但是,从并发系统性能的角度考虑,你觉得在这个事务序列里,应该先插入操作记录,还是应该先更新计数表呢?

先插入新纪录,因为插入新记录只会影响到行锁和间隙锁,而先更新计数表会占用计数表的写锁,而很多其他事务的插入操作就必须阻塞等待

四、评论中的精华
🏁:5.MySQL 什么是幻读?如何解决?
文章中的案例中并没有幻读现象
请看下一篇文章分析

[https://blog.csdn.net/hxy_lbj/article/details/115125950](14-01 | MySQL 幻读如何解决?)

👋:对于 count(主键 id) ,server层拿到ID,判断ID是不可能为空的按行累加。这个地方,是不是又点问题,既然是主键ID,是一定不会为空的,这个server层还需要判断不为空吗
答:代码就是这么写的 我也觉得可以优化一下… 不过现在就这样

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值