Mysql 系列 | count(*)

项目中常常需要统计表数据总数,select count(*) from t;

在 InnoDB 搜索引擎中,count(*) 需要把数据都读到内存中然后计数,数据很多时会很慢。

count 字段或者 ID,会不会好一些呢!实际上在 Mysql 中,count(字段) < count(主键id) < count(1) ≈ count(*),也就是说 count(*) 是最佳方案。

除此之外,可以考虑把计数保存起来,缓存或者数据库。

count(*) 的实现方式

  • MyISAM 中,会把表的总行数存在磁盘中,可以直接读出来,效率很高。

  • InnoDB 中,有多版本并发控制(MVCC),可重复读时默认隔离级别。每个事务中读取的行数不一定相同,所以每次查询的时候,需要一行一行读取依次判断可见性,并计数。

  • InnoDB 中,count(*) 已经被优化过。优化器会选择最小的索引树遍历,在保证逻辑正确的情况下,尽量减少数据的扫描行数。

  • show table status 命令取到的 table_rows 是采样估算的结果,误差可能达到 40% - 50%,不能直接使用。

缓存系统保存计数

  • 把计数存在 Redis 中,表中每添加一条数据,更新一次 Redis 的值

  • 数据库中表数据插入过程中,Redis 异常重启,会导致数据丢失。不过可以在重启后,去表中 count 得到正确的数值更新到 Redis 中。

  • Redis 正常工作时,因为 InnoDB 的 MVCC 机制,每个事务应该读到的总行数是不同的。而从 Redis 中则只能读到最新统计的行数,也是不精确的

数据库保存计数

  • 表中新增数据时,更新计数表中的数据

  • 因为实在同一存储系统,可以保证逻辑一致性。不过每次需要从表中查得计数值。

不同 count 的用法

count() 是个聚合函数。对于返回的结果集,判断参数不为 NULL 时统计值加一,否则不加。最终返回统计结果。

count(主键 ID)

  • InnoDB 会遍历整张表,取出 ID 返回给 Server 层,判断是不可能为空的,然后按行累加。

count(1)

  • 遍历整张表,但不取数据。Server 层对于返回的每一行数据,放一个数字 1,判断不可能为空,逐行累加。

  • 相比 count(ID) 更快。没有取数据的步骤,减少了从引擎返回 ID 后解析数据,拷贝字段值的操作。

count(字段)

  • 字段定义为 not null,从记录中读出字段,判断不能为空,逐行累加。

  • 字段定义为null,要把字段取出来,判断不为空,才会累加。

count(*)

  • 已经被优化过,不会取值,肯定不为空,逐行累加。
posted @ 2022-08-10 14:59  菜乌  阅读(433)  评论(0编辑  收藏  举报