Count(*)在不同引擎的实现方式
MyISAM引擎中
MyISAM引擎把一个表的总行数存储在了磁盘上,因此count(*)的时候会直接返回这个数,效率很高。但这是不加where条件的情况下,如果加了where条件的话,MyISAM引擎也不会这么快。
InnoDB引擎中
InnoDB引擎并没有把表的总行数存储在磁盘上,每次执行count都要把表的记录一行一行的从引擎里边读出来,然后累积计数。
为何InnoDB引擎不把表的总行数存储在磁盘?
因为InnoDB引擎是支持事务的,多版本并发控制MVCC,如果同一时刻有多个事务查询的话,那么InnoDB表也不知道应该返回多少行。
如下三个会话,如果表中有1000行的话,那么查询结果应该是
SessionA:1000 SessionB:1002 SessionC:1001
| SesssionA | SesssionB | SesssionB |
|---|---|---|
| begin | insert into | |
| begin | ||
| insert into | ||
| select count(*) | select count | select count |
| commit | commit |
InnoDB对Count(*)做的优化
InnoDB是索引组织表,主键索引树的叶子节点存储的是行记录的数据,普通索引树的叶子节点存储的是主键值。因此普通索引树比主键索引树要小很多,对于Count()这种操作不管是扫描普通索引树还是主键索引树得到的行数都是一样的,因此InnoDB会找到最小的那颗索引树来扫描遍历。在保证逻辑正确的前提下,尽量减少扫描的数据量,是数据库设计的通用法则之一。
*
- MyISAM count(*)执行快,但是不支持事务
- show table status,执行快,但是不准确
- InnoDB 执行慢,但是准确
计数方案
1、用缓存计数
可以使用redis用来数据,每次插入一条记录,redis中的计数就加1,每被删除一行数据,计数就减1。问题:如果既需要查询出最近100行记录的信息,又需要查询出总记录数,就需要先到数据库把100行记录取出来,然后再去redis中取出来总记录数。可能会出现数据不一致的情况。
如下
| session A | sessionB |
|---|---|
| 插入一行记录 | |
| 查询前100条记录 从redis取出总记录数 |
|
| redis +1 |
sessionB查询到的100条记录是最新的,但是redis还没有更新,总记录数是不正确的,因此数据不一致
2、用数据库计数
新增一张表来存储记录数,也会出现上边的情况,但是是可以解决的,因为InnoDB是支持事务的,可以利用事务的隔离性,将数据库的隔离级别设为RR,利用一致性视图
| sessionA | sessionB |
|---|---|
| begin | |
| 插入一条记录 | |
| 从表中取出前100行记录(因为事务的可重复读,是读不到sessionA插入的记录的) 从计数表中取出记录数 |
|
| 将表中记录数+1 |
不同Count的用法
count(字段)—字段值为null不计数
- 如果这个字段定义为“not null”的话,一行一行的从记录中取出来,判断不能为Null,然后累加
- 如果这个字段定义为允许为“null”的话,那么执行的时候判断有可能是null,还是要把值取出来再判断一下,不是null才累加
count(主键)
InnoDB会遍历整张表,把每一行的主键都取出来给server层,server层拿到主键的值之后,判断不为空,然后累加。count(1)
InnoDB会遍历整张表,但是不取值,server层对于返回的每一行,放一个数字1进去,判断是不可能为空的,按行累加。count(*)
不会把所有字段取出来,专门做了优化,不取值。count(*)肯定不是null,按行累加。各种Count的效率
count(主键)<count(字段)<count(1)≈count(*)
问题
刚刚说了可以通过事务来确保计数准确,不管是先插入还是先更新都可以保证数据读取的一致性,但是从并发系统的角度来看,是应该先更新还是先插入呢?
答案:先插入,再更新。因为更新需要获得行锁,多个线程并发执行的话,会涉及锁的竞争,如果先更新的话,那么锁会知道事务提交才会释放,如果后更新的话,那么持有锁的时间就相对较短,可以避免事务之间的锁等待,提升了并发度。
1.
count()的实现方式在MySAM引擎和InnoDB引擎的实现方式各是怎么样的? 为什么会有这种不同
2.
使用缓存保存count总数存在什么问题?
3.
如果使用一场单独的表来记录其他各张表的记录数的话,是怎么解决统计结果不精确的问题的?
4.
count(字段),count(id),count(1), count()各自是怎么样的执行机制, 效率排序是怎么样的?
