Innodb count的内部流程,你知道吗?

在Innodb可重复读隔离级别下,对count(字段)、count(主键 id)、count(1)、count(*)四种方案,我们应如何进行选择?

1.原因

1.1为什么有四个count

count定义:count()是一个聚合函数,对于返回的结果集,一行行地判断,如果 count 函数的参数不是 NULL,累计值就加 1,否则不加。最后返回累计值。

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

根据定义能推断出,使用count(*)、count(主键 id) 和 count(1)获得的结果是相同的,count(字段)因为”字段“可能不为NULL,所以结果存在与其它三个不一致的可能。

虽有4个count,其实对应相同函数。

1.2count有时很慢

很多同学有直观感受,当表比较大时,count速度很慢,有时需要几十秒甚至几分钟,但MyISAM能快速返回值。

主要原因为:

  • MyISAM会保存表的具体行数,因此这段代码在MyISAM存储引擎中执行,只需读出保存好的行数即可。

  • Innodb不保存具体行数,count需遍历全表,一行一行获取。之所以这么做,是因为MVCC需判断对当前的count语句,哪些行可见,哪些行不可见,具体细节可看Innodb事务隔离性实现原理,你了解吗?

2.count流程

count操作涉及MySQL的Server层和存储引擎层。Server负责判断与计数, Innodb存储引擎负责获取数据集。

Innodb为啥要刷脏页?里讲过,读取数据时,需要把数据从磁盘读到内存,所以count耗时的节点有如下几个:

  1. 将数据从磁盘读入内存,这会发生了很多次I/O,因此造成了主要的时间消耗

  2. 将数据从内存读出,放入返回数据集。引擎返回数据集会涉及到解析数据行,以及拷贝字段值

  3. server层对数据集进行NULL判断,进行计数操作

由此可分析出影响性能的几个方面:

  1. 从磁盘读入内存的数据量

  2. 是否需要将数值从内存中取出

  3. server层是否需要进行NULL判断

3.性能区别

根据以上几点,我们看一下count(字段)、count(主键 id)、count(1)、count(*)的区别。

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

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

对于 count(字段) 来说:

  • 如果这个“字段”是定义为 not null 的话,一行行地从记录里面读出这个字段,判断不能为 null,按行累加;

  • 如果这个“字段”定义允许为 null,那么执行的时候,判断到有可能是 null,还要把值取出来再判断一下,不是 null 才累加。

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

总结下各个操作:

操作是否取值是否判断备注
count(*)最快
count(1)
count(主键id)可能使用最小的索引树
count(字段)字段上无索引时,只能选主键索引

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

count(字段)<=count(主键 id)是因为count(主键 id)极有可能选择最小的非聚簇索引,而count(字段)如果“字段”没有索引的话,只能选主键索引,这样加载到内存的数据量就大了很多。另外两者有可能选用同一索引树。

当然,count(主键id)选用最小索引树有一个前提,查询语句中不包含where条件和group by条件。

其实count()、count(1)、count(主键id)可做相同优化,都选用最小索引树、不取值、无需判断的方案,不过目前只有count()做了这个优化。

大家count的时候,尽量选择count(*)即可。

4.优化

count()已是最快的方案了,如果还不满足条件,如何优化count()性能呢?

有两个方案:

  1. 创建一个最小索引,这样count(*)计算时会选择最小索引,减少IO

  2. 利用事务,自己计数。建立计数表,增加或删除数据时,同时更新计数表。

总结

大家对count的了解相对多一些,不过仍有很多小细节值得思考。

资料

  1. innodb中count(*),count(字段),count(主键id),count(1)的区别

  2. MySQL学习笔记:count(1)、count(*)、count(字段)的区别

  3. MySQL 全表 COUNT(*) 简述

  4. 在MySQL的InnoDB存储引擎中count(*)函数的优化

  5. MySQL InnoDB count()函数

最后

大家如果喜欢我的文章,可以关注我的公众号(程序员麻辣烫)

我的个人博客为:https://shidawuhen.github.io/

图片

往期文章回顾:

  1. 设计模式

  2. 招聘

  3. 思考

  4. 存储

  5. 算法系列

  6. 读书笔记

  7. 小工具

  8. 架构

  9. 网络

  10. Go语言

### Count 值为 0 的原因及解决办法 #### Oracle 中 `COUNT(*)` 结果为 0 的情况 在 Oracle 数据库中,如果执行 `SELECT COUNT(*) FROM table_name` 返回结果为 0,可能是因为表中的数据存在逻辑上的不可见状态。例如,在某些情况下,未提交的数据或者被删除但尚未清理的记录可能导致统计结果异常[^4]。此外,索引损坏也可能影响计数操作的结果。 对于这种情况,可以尝试以下几种方式来解决问题: 1. **重新计算统计数据**:通过运行 `ANALYZE TABLE table_name COMPUTE STATISTICS` 或者使用更现代的方法 `DBMS_STATS.GATHER_TABLE_STATS('schema', 'table')` 来刷新表的统计信息。 2. **重建索引**:如果有依赖于索引的操作失败,可以通过 `ALTER INDEX index_name REBUILD` 进行修复。 3. **检查事务隔离级别**:确认当前会话是否存在未提交的更改,这些更改可能会隐藏部分数据。 --- #### MySQL InnoDB 中 `COUNT(*)` 性能问题与准确性不足的情况 MySQL 使用 InnoDB 存储引擎时,由于其多版本并发控制 (MVCC) 特性以及存储结构设计,`COUNT(*)` 可能表现出性能较差或结果不够精确的现象。具体表现为当大量数据频繁更新时,InnoDB 不会对每条记录都实时维护全局计数值,而是基于近似估算得出结果[^1]。 针对这一现象,可采取如下措施改善效率并提高精度: - **创建覆盖索引**:利用特定字段建立辅助索引来加速查询过程,比如 `(id)` 字段上构建唯一索引。 - **调整配置参数**:适当修改 `innodb_stats_auto_recalc` 和其他相关选项以确保及时更新内部统计信息。 - **采用缓存机制**:引入 Redis 等外部工具保存总数量副本,减少直接访问数据库的压力。 --- #### Hive 表中 `COUNT(1)` 或 `COUNT(*)` 显示为零的情形及其应对策略 Hive 是一种分布式大数据处理框架,它的工作原理决定了并非每次调用都能立即获取到最新版的真实值。特别是当源文件仅通过 LOAD DATA 方式导入而非 INSERT INTO 插入时,元数据层面上并未同步更新 row counts 记录,从而造成最终呈现出来的总数度量偏低甚至归零[^2]。 以下是几个可行方案用于修正此类偏差: 1. **手动触发分析命令**:运用 `ANALYZE TABLE your_table COMPUTE STATISTICS` 更新整张表格的基础属性描述符; 2. **强制全盘扫描模式**:设置环境变量 `set hive.compute.query.using.stats=false;` 关闭预估功能转而依靠实际遍历完成总计工作流; 3. **转换成 ACID 支持型格式**:将原始资料迁移到支持事务特性的 ORC 文件形式下再做进一步加工运算即可获得更加稳定可靠的反馈效果。 --- #### 数据倾斜引发的潜在隐患及缓解途径 除了上述提到的技术层面因素外,还有一种常见场景也会致使汇总函数输出为空白——即所谓的“数据倾斜”。这通常发生在分组聚合阶段因键分布极度不平衡而导致少数分区承载过多负载进而超时崩溃退出之前未能成功交付任何有效成果给下游消费者知晓[^3]。 为了预防这种状况发生可以从以下几个角度切入考虑改进计划: - 调优 SQL 编写技巧规避不必要的复杂嵌套关系降低整体开销成本; - 利用采样技术预先评估各维度规模差异程度据此分配资源配额比例达到均衡目的; - 配合广播变量传递小尺寸集合成员列表实现高效匹配查找替代传统 JOIN 方法提升响应速度和服务质量水平等等…… --- ### 示例代码片段展示如何优化 Hive 查询语句 ```sql -- 手动启动详尽统计流程 ANALYZE TABLE sales_data PARTITION(year=2023) COMPUTE STATISTICS; -- 修改默认行为禁用快速猜测算法 SET hive.compute.query.using.stats = false; SELECT COUNT(*) AS total_records FROM customer_orders; ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员麻辣烫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值