Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
git-zjx opened this issue Jul 26, 2019 · 0 comments

Comments

@git-zjx
Copy link
Owner

commented Jul 26, 2019

count(*) 的实现方式

  • MyISAM 引擎把一个表的总行数存在了磁盘上,因此执行 count(*) 的时候会直接返回这个数,效率很高;
  • InnoDB 引擎执行 count(*) 的时候,需要把数据一行一行地从引擎里面读出来,然后累积计数。

为什么 InnoDB 不跟 MyISAM 一样,也把数字存起来呢?

即使是在同一个时刻的多个查询,由于多版本并发控制(MVCC)的原因,InnoDB 表“应该返回多少行”也是不确定的,每一行记录都要判断自己是否对这个会话可见

InnoDB 对 count(*) 的优化

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

其他计数方法

  1. 缓存计数
    丢失更新,逻辑不精确(在并发系统里面,无法精确控制不同线程的执行时刻的)
  2. 数据库计数
    利用事务存储计数

不同的 count 用法

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

分析性能差别的时候,你可以记住这么几个原则:

  • server 层要什么就给什么;
  • InnoDB 只给必要的值;
  • 优化器只优化了 count(*) 的语义为“取行数”,其他“显而易见”的优化并没有做。

对于 count(主键 id) 来说,InnoDB 引擎会遍历整张表,把每一行的 id 值都取出来,返回给 server 层。server 层拿到 id 后,判断是不可能为空的,就按行累加。
对于 count(1) 来说,InnoDB 引擎遍历整张表,但不取值。server 层对于返回的每一行,放一个数字“1”进去,判断是不可能为空的,按行累加。
对于 count(字段) 来说,如果这个“字段”是定义为 not null 的话,一行行地从记录里面读出这个字段,判断不能为 null,按行累加;如果这个“字段”定义允许为 null,那么执行的时候,判断到有可能是 null,还要把值取出来再判断一下,不是 null 才累加。
对于 count(*) 来说, 并不会把全部字段取出来,而是专门做了优化,不取值,按行累加

结论是:按照效率排序的话,count(字段)<count(主键 id)<count(1)≈count(),所以建议尽量使用 count()

问题

  1. 什么时候使用 alter table t engine=InnoDB 会让一个表占用的空间反而变大?
  • 表已经没有空洞了
  • 在 DDL 期间,如果刚好有外部的 DML 在执行,这期间可能会引入一些新的空洞
  • 在重建表的时候,InnoDB 不会把整张表占满,每个页留了 1/16 给后续的更新用。也就是说,其实重建表之后不是“最”紧凑的

@git-zjx git-zjx added this to MySQL实战45讲 in MySQL Aug 1, 2019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
1 participant
You can’t perform that action at this time.