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

Page content

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

count(*)在不同引擎的实现方式

  • MyISAM 引擎单独存储了表的总行数,执行 count(*) 时可以直接返回,效率高。(查询不能带有where条件)
  • InnoDB 执行 count(*),需要将数据从引擎里一行一行地读出来进行计数

为什么 innodb 不像 myisam 一样单独存储总行数

因为 MVCC。 在同一时刻的多个查询,由于多版本并发控制的原因,应该返回多少行是不一致的

innodb对count(*)做了哪些优化

对于 count(*) 这中操作,遍历数据表的任何一棵索引树结果都是一样的,因此 MySQL 优化器会选择最小的那棵树来进行遍历。一般不会是主键索引树,因为它的叶子节点存储的是数据,而普通索引树的叶子节点存储的是主键id,数据量会小很多。

在保证逻辑正确的前提下,尽量减少扫描的数据量,是数据库系统设计的通用法则之一。

show table status 返回的 table_rows 为什么不能使用

不能。TABLE_ROWS 是通过采样估算得来的,不准确。

计数的实现方案

用缓存系统保存计数

问题: 缓存系统可能会丢失更新; 逻辑上可能不精确;

两个不同的存储构成的系统,不支持分布式事务,无法拿到精确一致的视图

数据库保存计数

count() 的语义

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

count(*)、count(主键 id)、count(1)、count(字段)

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

问题讨论

从并发系统性能的角度考虑,你觉得在这个事务序列里,应该先插入操作记录,还是应该先更新计数表呢?