MySQL_count(?)效率对比

count(*)慢的原因

MyISAM引擎是把一个表的总行数存在了磁盘上,因此count(*)的时候回直接返回这个数,效率很高**(当有where条件的时候,不会这么快)**

InnoDB,在count(*)的时候需要吧数据一行一行从引擎里面读出来,然后累计计数

学习检测

  1. MyISAM 和InnoDB count(*)的区别?

  2. InnoDB count(*)是怎么做的优化?

  3. count的几个效率和过程?

  4. show table status 中的TABLE_ROWS 可用吗?

总结

  1. MyISAM是在表中有计数,InnoDB是没有的,因为InnoDB是支持事务的(MVCC)不同场景视图一致性读不同

  2. InnoDB的count(*)是会选择最小的索引树进行遍历,获取总行数,判断行不为null,计数加1的操作

  3. count(字段),如果定义为not null ,取出行数据,判断字段是否为空,不为空,累计加1

    count(主键id),遍历整张表,取出id,返回给server层,判断不为空,计数加1

    count(1),遍历整张表,取出行数据,判断不为空,计数加1

    count(*),遍历最小索引树,取行数据,不为空,加1

    id 和字段区别在于,id会选择最小的索引树(聚簇索引)

4.不可用,这是也是扫描行数的估值

InnoDB为什么不和MyISAM一样存储count(*)

这是因为即使是在同一个时刻的多个查询,由于多版本并发控制(MVCC)的原因,InnoDB 表“应该返回多少行”也是不确定的。这里,我用一个算 count(*) 的例子来为你解释一下。

假设表 t 中现在有 10000 条记录,我们设计了三个用户并行的会话。

  • 会话 A 先启动事务并查询一次表的总行数;

  • 会话 B 启动事务,插入一行后记录后,查询表的总行数;

  • 会话 C 先启动一个单独的语句,插入一行记录后,查询表的总行数。

我们假设从上到下是按照时间顺序执行的,同一行语句是在同一时刻执行的。

img

你会看到,在最后一个时刻,三个会话 A、B、C 会同时查询表 t 的总行数,但拿到的结果却不同。

这和 InnoDB 的事务设计有关系,可重复读是它默认的隔离级别,在代码上就是通过多版本并发控制,也就是 MVCC 来实现的。每一行记录都要判断自己是否对这个会话可见,因此对于 count(*) 请求来说,InnoDB 只好把数据一行一行地读出依次判断,可见的行才能够用于计算“基于这个查询”的表的总行数

count(*)的优化

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

show table status TABLE_ROWS 不准确

索引统计的行数值是通过采样来估算的。实际上,TABLE_ROWS 就是从这个采样估算得来的,因此它也很不准。有多不准呢,官方文档说误差可能达到 40% 到 50%。所以,show table status 命令显示的行数也不能直接使用

到这里我们小结一下:

  • MyISAM 表虽然 count(*) 很快,但是不支持事务;

  • show table status 命令虽然返回很快,但是不准确;

  • InnoDB 表直接 count(*) 会遍历全表,虽然结果准确,但会导致性能问题。

不同的count用法

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

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

单看这两个用法的差别的话,你能对比出来,count(1) 执行得要比 count(主键 id) 快。因为从引擎返回 id 会涉及到解析数据行,以及拷贝字段值的操作。

对于 count(字段) 来说:

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

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

也就是前面的第一条原则,server 层要什么字段,InnoDB 就返回什么字段。

但是 count(*) 是例外) 肯定不是 null,按行累加。

count效率排行

count(字段)<count(主键 id)<count(1)≈count(*)

count(字段)要取行中的字段,判断是否为null

count(主键id)要去行数据,到server中判断是否为null

count(1)直接判断行数据是否为空

count(*)去最小索引树,判断行是否为空

count(id)可能会选择最小的索引来遍历
而count(字段)的话,如果字段上没有索引,就只能选主键索引

发布了48 篇原创文章 · 获赞 31 · 访问量 4554

猜你喜欢

转载自blog.csdn.net/qq_39787367/article/details/103861957
今日推荐