Mysql 多种Count写法的区别

今天咱们来看看count的不一样实现方式数据库

count(*) 的实现方式

先来看一下 coun(*)的实现,MyISAM和InnoDB的实现上是不一样的缓存

MyISAM 引擎把一个表的总行数存在了磁盘上,所以执行 count() 的时候会直接返回这个数,效率很高; 而 InnoDB 引擎就麻烦了,它执行 count() 的时候,须要把数据一行一行地从引擎里面读出来,而后累积计数并发

若是加了 where条件的话,MyISAM 表也是不能返回得这么快的函数

为何 InnoDB 不跟 MyISAM 同样,也把数字存起来呢?性能

这是由于即便是在同一个时刻的多个查询,因为多版本并发控制(MVCC)的缘由, InnoDB 表应该返回多少行也是不肯定的优化

这和 InnoDB 的事务设计有关系,可重复读是它默认的隔离级别,在代码上就是经过MVCC 来实现的。每一行记录都要判断本身是否对这个会话可见,所以对于 count(*) 请求来讲,InnoDB只好把数据一行一行地读出依次判断,可见的行才能 够用于计算“基于这个查询”的表的总行数线程

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

若是你用过 show table status 命令的话,就会发现这个命令的输出结果里面也有一个 TABLE_ROWS 用于显示这个表当前有多少行,这个命令执行挺快的,那这个 TABLE_ROWS 能代替 count(*) 吗server

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

不一样的 count 用法区别

咱们再来看看不一样count用法的区别

首先你要弄清楚 count() 的语义。count() 是一个聚合函数,对于返回的结果集, 一行行地判断,若是 count 函数的参数不是 NULL,累计值就加 1,不然不加。最后返回 累计值

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

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

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

对于 count(字段) 来讲:

  1. 若是这个“字段”是定义为 not null 的话,一行行地从记录里面读出这个字段,判断不 能为 null,按行累加;
  2. 若是这个“字段”定义容许为 null,那么执行的时候,判断到有多是 null,还要把值 取出来再判断一下,不是 null 才累加。

可是 count() 是例外,并不会把所有字段取出来,而是专门作了优化,不取值。 count() 确定不是 null,按行累加

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

计数解决方案

每次都调用count来计算比较消耗性能

对于更新很频繁的库来讲,你可能会第一时间想到,用缓存系统来支持。

这种方式下,读和更新操做都很快,但你再想一下 这种方式存在什么问题吗?

没错,缓存系统可能会丢失更新。

将计数保存在缓存系统中的方式,还不仅是丢失更新的问题。即便 Redis 正常工做,这个值仍是逻辑上不精确的

在并发系统里面,咱们是没法精确控制不一样线程的执行时刻的,因此,咱们说即便 Redis 正常工做,这个计数值仍是逻辑上不精确的

在数据库保存计数能够解决这个问题,咱们能够经过事务来保证一致性

相关文章
相关标签/搜索