登录
/
注册
首页
论坛
其它
首页
科技
业界
安全
程序
广播
Follow
关于
导读
排行榜
资讯
发帖说明
登录
/
注册
账号
自动登录
找回密码
密码
登录
立即注册
搜索
搜索
关闭
CSDN热搜
程序园
精品问答
技术交流
资源下载
本版
帖子
用户
软件
问答
教程
代码
写记录
写博客
小组
VIP申请
VIP网盘
网盘
联系我们
发帖说明
道具
勋章
任务
淘帖
动态
分享
留言板
导读
设置
我的收藏
退出
腾讯QQ
微信登录
返回列表
首页
›
业界区
›
业界
›
MySQL 14 count(*)这么慢,我该怎么办?
MySQL 14 count(*)这么慢,我该怎么办?
[ 复制链接 ]
笙芝
2025-7-14 10:59:19
程序园永久vip申请,500美金$,无限下载程序园所有程序/软件/数据/等
count(*)的实现方式
在不同的MySQL引擎中,count(*)有不同的实现方式:
MyISAM引擎把一个表的总行数存在磁盘上,执行count(*)时能直接返回总行数,效率很高;
InnoDB引擎需要把数据一行一行从引擎里读出来,然后累积计数。
需要说明,本文讨论没有过滤条件的count(*),如果加了where条件,MyISAM也没有那么快。
InnoDB为什么不和MyISAM一样设计,把行数给存起来呢?
这是因为即使是在同一刻的多个查询,由于MVCC的原因,返回多少行也不是确定的。假设现在表t中有10000条记录,设计了三个用户并行的会话:
会话A先启动事务并查询一次表的总行数;
会话B启动事务,插入一行记录后,查询表的总行数;
会话C先启动一个单独的语句,插入一行记录后,查询表的总行数。
假设该过程的时间顺序如下:
可以看到,三个会话在同一时刻查询总行数,得到的结果却不同。至于为什么查询结果如上,可以从MySQL 08那篇文章得到答案。
这是由于InnoDB的默认隔离级别是可重复读,在代码上通过MVCC实现。由于每一行记录都需要判断自己是否对这个会话可见,因此对于count(*)请求来说,InnoDB需要把数据一行一行读出依次进行判断,只有可见的行才能计入总行数。
不过MySQL还是对count(*)做了一定的优化。由于普通索引树比主键索引树小很多,而count(*)操作遍历哪个索引树得到的结果在逻辑上都相同,因此优化器会找到最小的索引树来遍历。在保证逻辑正确的前提下,尽量减少扫描的数据量,是数据库系统设计的通用法则之一。
有个命令叫show table status,其输出结果里有一个TABLE_ROWS用来显示这个表当前有多少行。这个命令执行速度较快,那能否用它加速计算行数呢?
是不行的,TABLE_ROWS的值是从MySQL 10中提到的采样值估算而得的,因此也非常不准确。
那么如果需要经常用到记录总数的值,即需要加快count(*),就需要自己计数,基本思路是
找一个地方,把记录表的行数存起来
。
用缓存系统保存计数
对于更新频繁的库来说,可能会想到用缓存系统来支持。比如用Redis保存总行数,每插入一行Redis的计数就加1,每删除一行Redis计数就减1。
这种设计速度确实很快,但缓存系统可能丢失更新。若想找地方将值持久化存储,仍然可能丢失更新。比如刚在数据表中插入一行,Redis中保存的值也加了1,然后Redis异常重启,而此时加1还未持久化。
当然,异常重启是有解的,可以在异常重启后再执行一次count(*)获取真实的最新值,再写回Redis。异常重启毕竟不是经常出现的情况,这样偶尔的情况是可以接受的。
不过,即使Redis正常工作,这个值依旧是
逻辑上不精确
的。
设想有这样一个页面,要显示操作记录的总数,同时还要显示最近操作的100条记录,那么就需要先到Redis里取出计数,再到数据表里取记录。
不精确的定义如下:
查到的100条记录里有最新的插入记录,但Redis里计数还未加1;
或是查到的100条记录里没有最新的插入记录,但Redis计数已经加1。
来看两个例子:
上面的两个例子,就对应了不精确的两种情况。
由于并发系统里无法精确控制不同线程的执行时刻,可能出现上述两种情况,因此说即使Redis正常工作,计数值还是逻辑上不精确的。
在数据库保存计数
既然不能用缓存系统保存,如果把计数直接放到数据库里单独的一张计数表C中会怎么样?
首先,解决了崩溃丢失的问题,因为InnoDB支持崩溃恢复。
其次,由于事务的特性,也能解决不精确的问题。
在上图中,会话B在T3执行读操作,但此时更新事务还未提交,因此计数值+1这个操作对会话B不可见,从而会话B查计数值和“最近100条记录”看到的结果在逻辑上一致。
不同的count用法
最后说一下count()括号内填不同内容的几种用法。
count()是一个聚合函数,对于返回的结果集,一行一行判断,如果count()函数参数对应的值不为NULL,累计值就加1,否则不加,最后返回累计值。
因此,count(*)、count(主键id)、count(1)都表示返回满足条件的结果集的总行数,而count(字段)表示返回满足条件的数据行中,“字段”这一列的值不为NULL的总个数。
而分析性能差别时,可以记住以下几个原则:
Server层要什么就给什么;
InnoDB只给必要的值;
优化器只优化了count(*)的语义为“取行数”,其他优化并没有做。
如何理解呢?具体看看:
对于count(主键id),InnoDB会遍历整张表,取出每一行的id,返回给Server层。Server层拿到id,判断不可能为空,按行累加;
对于count(1),InnoDB会遍历整张表但不取值,Server层对返回的每一行,放一个数字“1”进去并判断不可能为空,按行累加;
对于count(字段):
如果字段定义为not null,Server层能直接判断不能为null,按行累加;
如果字段定义允许null,Server层需要把值取出来再判断一下,不是null才累加;
对于count(*),进行了优化,InnoDB遍历整张表但不取值,Server会判断肯定不是null,按行累加。
因此,如果按照效率排序,count(字段)
MySQL
count
这么
我该
怎么办
相关帖子
AlmaLinux下mysql 8安装与数据迁移
Ubuntu下,MySQL密码遗失时修改密码
Ubuntu下,MySQL修改端口号
MySQL 筛选条件放 ON 后 vs 放 WHERE 后
同事写的count(*)性能很差,如何优化?
为什么 Iceberg 在数据湖领域这么火
记一次MySQL binlog日志导致磁盘空间占满的问题
记一次MySQL binlog日志导致磁盘空间占满的问题
【面试题】MySQL 索引的最左前缀匹配原则是什么?
【面试题】MySQL 的存储引擎有哪些?它们之间有什么区别?
回复
使用道具
举报
提升卡
置顶卡
沉默卡
喧嚣卡
变色卡
千斤顶
照妖镜
相关推荐
安全
AlmaLinux下mysql 8安装与数据迁移
1
343
菅舛
2025-12-07
安全
Ubuntu下,MySQL密码遗失时修改密码
1
876
徙办
2025-12-07
安全
Ubuntu下,MySQL修改端口号
2
128
痨砖
2025-12-07
安全
MySQL 筛选条件放 ON 后 vs 放 WHERE 后
1
956
热琢
2025-12-10
业界
同事写的count(*)性能很差,如何优化?
3
620
僻嘶
2025-12-11
业界
为什么 Iceberg 在数据湖领域这么火
0
252
季卓然
2025-12-16
安全
记一次MySQL binlog日志导致磁盘空间占满的问题
0
896
荆邦
2025-12-17
安全
记一次MySQL binlog日志导致磁盘空间占满的问题
0
796
跟尴
2025-12-17
安全
【面试题】MySQL 索引的最左前缀匹配原则是什么?
0
725
荪俗
2025-12-19
安全
【面试题】MySQL 的存储引擎有哪些?它们之间有什么区别?
0
587
焦尔蕾
2025-12-19
回复
(3)
釉她
2025-11-12 17:37:45
回复
使用道具
举报
照妖镜
猛犸象科技工作室:
网站开发,备案域名,渗透,服务器出租,DDOS/CC攻击,TG加粉引流
新版吗?好像是停更了吧。
洪思思
2025-11-28 10:25:34
回复
使用道具
举报
照妖镜
程序园永久vip申请,500美金$,无限下载程序园所有程序/软件/数据/等
鼓励转贴优秀软件安全工具和文档!
挫莉虻
2025-12-3 01:54:27
回复
使用道具
举报
照妖镜
程序园永久vip申请,500美金$,无限下载程序园所有程序/软件/数据/等
懂技术并乐意极积无私分享的人越来越少。珍惜
高级模式
B
Color
Image
Link
Quote
Code
Smilies
您需要登录后才可以回帖
登录
|
立即注册
回复
本版积分规则
回帖并转播
回帖后跳转到最后一页
浏览过的版块
安全
签约作者
程序园优秀签约作者
发帖
笙芝
2025-12-3 01:54:27
关注
0
粉丝关注
20
主题发布
板块介绍填写区域,请于后台编辑
财富榜{圆}
3934307807
991124
anyue1937
9994892
kk14977
6845357
4
xiangqian
638210
5
韶又彤
9997
6
宋子
9981
7
闰咄阅
9993
8
刎唇
9993
9
俞瑛瑶
9998
10
蓬森莉
9950
查看更多
今日好文热榜
960
MAUI库推荐二:MPowerKit
959
.NET10 New feature 新增功能介绍-JIT编译
402
报考陪诊师选守嘉陪诊的理由
270
C#AI系列(7):从零开始LLM之Tokenizer实现
282
C#AI系列(7):从零开始LLM之Tokenizer实现
823
Pytest 测试用例自动生成:接口自动化进阶
846
一天一个Python库:NumPy - 科学计算的基石
14
FFmpeg 内存输入&输出
743
一文读懂RAG架构如何助力AI
499
一文读懂RAG架构如何助力AI
545
ROS2之TF介绍
398
SW 随笔 001 — InlineArray 带你飞(Since
692
当遇见 CatchAdmin V5-模块化设计重新定义
494
【有手就行】SWIFT:花20分钟把大模型的名
562
论文速读记录 | 2025.12(2)
376
浮点数的本质:为什么计算机无法精确表示0.
729
Flink源码阅读:如何生成JobGraph
931
Python 潮流周刊#132:30 年 Python 自由职
483
大模型榜单周报(2025/12/20)
159
【节点】[LinearToGammaSpaceExact节点]原