count(?)为什么越来越慢?

在日常的业务开发中,汇总记录使用select count(*) from t是常见用法,分页查询的时候都会用到,那么随着系统记录的数量越来越多,你有没有发现这条语句执行的也越来越慢了呢?

一、count(*)原理

在不同的存储引擎中,count(*)有不同的实现方式

  • MyISAM 存储引擎中,把一个表的总行数存在了磁盘上,因此执行 count(*)的时候,直接读取这个值就行了,效率很高
  • 而 InnoDB 执行 count(*)的时候,需要把一行一行数据从存储引擎里读取出来,然后累计计数

当然我们讨论的是没有带 where 语句的,如果带了 where 语句,MyISAM 返回的也不会这么快的

那么问题来了,为什么 InnoDB 不能想 MyISAM 一样,把总数存储起来,而要一行一行的读呢?

原因就是InnoDB是支持事务的,由于多版本并发控制(MVCC)的原因,InnoDB在不同时刻调用count(*)的数据是不确定的

这里举个例子,假设 T 表中有 10000 条数据,设计三个会话:

sessionA sessionB sessionB
select count(*) from t; (返回 10000)
insert into t (插入一行)
insert into t(插入一行)
select count(*) from t;(返回 10000) select count(*) from t;(返回 10002) select count(*) from t;(返回 10001)

可以看出不同时期的 count()返回的值是不一样的,每一行记录都要判断自己是否对这个会话可见,因此对 count()来说,InnoDB 只有把每一行都读出来做判断,可见性行的才能用于计算总行数

那么 InooDB 是怎么统计记录的呢?

InnoDB 是索引组织表,主键索引树的叶子节点存放的是数据,而普通索引的叶子节点存放的是主键值。所以这就导致了普通索引是比主键索引小很多的,对于 count(*)来说,遍历哪一个索引树在结果逻辑上都是一样的,因此MySQL优化器会找到最小的那一颗索引树来遍历,在保证逻辑结果正确的前提下,尽量减少扫描的数据量,是数据库系统设计的通用法则之一

二、几种 count(x)的区别

当然肯定有同学见过count(*),count(1),count(主键)count(字段)这几种用法,那么这几种 count 都有什么区别呢?

首先 count()是一个聚合函数,对于返回的结果集,一行一行的判断,如果 count 函数的参数不是 null,累计值就+1,否则不加,最终返回累计值

  • count(1):InnoDB 会扫描遍历整张表,但不取值。server 层对于返回的每一行,判断不为NULL的,进行+1累计
  • count(主键):InnoDB 会扫描遍历整张表,把每一行的 ID 都取出来,返回给 server 层,server 拿到 ID 后判断是否为 NULL,然后按行累计

但从两个用法的差距来看,count(1)会比count(主键)快一点,因为 count(主键)从引擎返回 id 会设计到解析数据行,以及拷贝字段值的操作

  • count(字段):InnoDB 一行行从记录中读取出这个字段,判断是否为NULL,不为NULL才累加
  • count(*): count(*)是例外,并不会把所有字段取出来,而是专门做了优化,不取值,count(*)肯定不为 NULL,按行累加

所以总结来说:count(*) ≈ count(1) > count(主键) > count(字段)


我是一零贰肆,一个关注Java技术和记录生活的博主。

欢迎扫码关注“一零贰肆”的公众号,一起学习,共同进步,多看路,少踩坑。

posted @   孙半仙人  阅读(82)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示

目录导航

剑桥
17°
20:14发布
剑桥
20:14发布
17°
多云
西南风
5级
空气质量
相对湿度
32%
今天
4°/17°
周三
多云
1°/9°
周四
雨夹雪
0°/3°