MySQL45讲之count操作

前言

本文介绍 MyISAM 和 InnoDB 如何执行 count 操作,如果是一个需要使用 count 进行大量计数的场景,应该如何设计实现,以及不同 count 操作的效率。

MyISAM和InnoDB的count

MyISAM

MyISAM 存储引擎的每个表记录了总行数,在没有 where 条件时,直接获取该记录值返回。

InnoDB

InnoDB 获取 count 值,只能通过扫描索引树来计数。

为什么 InnoDB 只能临时扫描来计数,而不能像 MyISAM 一样存储一个总行数值?
对于同一时刻的多个查询请求,因为并发版本控制的原因,InnoDB 表应该返回多少行是不确定的,需要扫描索引,判断每行记录的可见性。

此外,InnoDB 也做了一些优化。主键索引树存储了行记录,而普通索引树只存储主键值,所以普通索引树比主键索引树小很多。因此,MySQL 会优先选择最小的索引树来遍历。在保证逻辑正确的情况下,尽量减少扫描的数据量,是数据库系统设计的通用法则之一。

count值如何记录

1、缓存记录

比如在 Redis 中用 string 类型记录一个计数,当新增或者删除记录时,相应修改 Redis 的值。

这样是不行的,没法保证数据的一致性

首先,如果业务系统插入或删除一行数据后,系统宕机,Redis 没有写入,重启系统后 Redis 会与数据库不一致。不过,这个问题可以通过系统重启时从数据库查询一次解决。

而且,如果需要同时从 Redis 和数据库中查询数据,两者无法保证数据一致,比如从 Redis 中取出表总行数和从数据库中取出前 100 行数据。因为并发请求,只要从 Redis 和 MySQL 查询数据的操作不是原子的,数据就不是一致的。

2、数据库统计表记录

数据库新建一个统计表记录行数。那不是和前一个方法一样么,并发请求下得到的结果可能不一致。

可以的,行数据和统计数据同时存在数据库中,并且数据库支持事务,所以可以将多个操作封装成原子的,保证数据一致。

多种count操作的效率

count(主键id)

存储引擎遍历表拿到行记录返回,server 层解析出 id 值,判断是否为 null,统计行数。

count(1)

存储引擎遍历表不取值,server 层对于每一行放进去一个 1,判断是否为 null,统计行数。

因为 server 层需要解析引擎返回的结果拿到 id,所以 count(1) 比 count(主键id) 高效。

count(字段)

存储引擎遍历表取出字段返回,server 层解析,判断字段是否为 null,统计行数。

count(*)

MySQL 进行了优化,存储引擎遍历表不取值,server 层判断是否为 null,逐行累加。

效率从高到低:count(*) ≈ count(1) > count(主键id) > count(字段)

参考

上一篇:MySQL45讲笔记


下一篇:MySQL45讲之order工作原理