MySQL45讲之count操作flowers
本文介绍 MyISAM 和 InnoDB 如何执行 count 操作,如果是一个需要使用 count 进行大量计数的场景,应该如何设计实现,以及不同 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(字段)
提问
在刚刚讨论的方案中,我们用了事务来确保计数准确。由于事务可以保证中间结果不被别的事务读到,因此修改计数值和插入新记录的顺序是不影响逻辑结果的。
但是,从并发系统性能的角度考虑,你觉得在这个事务序列里,应该先插入操作记录,还是应该先更新计数表呢?
先执行插入操作,再执行更新操作。从并发系统性能的角度考虑,应该尽可能减少锁等待,而更新操作需要加行锁,行锁在事务提交之后才会释放。所以最后执行更新操作,减少锁等待,提高并发度。
参考
- [1] count这么慢,我该怎么办
以上是 MySQL45讲之count操作flowers 的全部内容, 来源链接: utcz.com/z/535938.html