count
# count
MyISAM:将表的行数存储在磁盘中,查询时直接取,速度快。
InnoDB:MVCC机制导致不同行记录对于不同事务的可见性都是不同的,因此InnoDB只能将一行一行读出来进行判断和计数,效率低。
InnoDB扫描表时会进行相应的优化,会选择普通索引树下的记录进行扫描,而不会选择主键索引树,因为相较之下主键索引叶子节点的数据量要大于普通索引,这样可以减少扫描的数据量。
# 1.count(*)实现方式
逻辑一致:插入数据后,表记录行数count和select读取到的记录是相互匹配的。不能出现行数增加了,但是这条数据查不到;或者是这条插入的记录可以被读取到,但是表记录数没有更新。
- Redis存放count(*)表的行数
存在问题:插入数据时,redis更新行数与数据库插入记录这两个操作不具有原子性,因此如果在redis更新和数据库表更新之间有并发读操作,那么得到的结果就会出现逻辑不一致。
- 将count(*)行数存入数据库中的一张单独的表
利用MySQL事务的特性,当前事务只有同时完成insert插入记录,以及表行数加一这两个操作后才提交事务,从而使插入操作和加一操作其它事务都可见。保证了两个操作的一致性。
💡考虑性能的话,先插入数据,再update更新计数表。因为在并发场景下,更新操作冲突时会加行锁,而计数表冲突的概率比行记录冲突的概率更高,因此把更新操作放在最后,可以减少更新表行记录锁的持有时间,从而最大程度减少锁等待。
# 2.不同count的用法
InnoDB引擎返回数据给server层时,遵循以下原则:server层要什么就给什么,并且InnoDB只会给必要的数据
# 2.1count(主键id)
- InnoDB引擎将每行的id都取出来,返回给server层
- server层判断如果不为空,则按行累加
# 2.2count(1)
- InnoDB引擎直接放回每行记录给server层(相比于count(主键)更快,因为少了解析取出id值这个过程)
- server层将“1”放进每一行数据,不为空则累加
# 2.3count(字段)
- InnoDB取出每行该字段的值给server层
- 无论该字段定义为null或者时not null。计算时只有字段的值非null才会进行累加。
# 2.4count(*)
InnoDB对count(*)进行了优化,不需要把全部字段取出来一个个判断是否为空,直接记录行数。
# 2.5总结
性能效率上,count(*)≈count(1)>count(id)>count(字段)
编辑 (opens new window)
上次更新: 2023/12/15, 15:49:57