这是我参与8月更文挑战的第8天,活动详情查看:8月更文挑战
count(*)的实现方式
- MyISAM - 表的总行数存在磁盘上 - 直接返回这个数 - 效率高【没有过滤条件的count(*)】
- InnoDB - 数据一行行从引擎中读出来 - 累积计数
为什么InnoDB不把数字也存起来?
由于多版本并发控制(MVCC) - InnoDB表“应该返回多少行”也不确定
InnoDB的事务涉及 - 默认隔离级别是可重复读 - MVCC实现 - 每行记录都要判断自己是否对这个会话可见 -- 数据一行行读出依次判断,可见的行才能用于计算“基于这个查询”的表的总行数。
InnoDB - 索引组织表 - 主键索引树的叶子节点是数据,普通索引树的叶子节点是主键值 - 普通索引树比主键索引树小很多 - count(*) 遍历哪个索引树的到的结果逻辑上都一样 - MySQL优化器会找到最小的那棵树来遍历 -在保证逻辑正确的前提下,尽量减少扫描的数据量 - 数据库系统涉及的通用法则之一
show table status命令输出结果中的TABLE_ROWS用于显示这个表当前有多少行
索引统计的值是通过采样来估算的 - TABLE_ROWS就是这个采样估算得来 - 因此不准
小结
- MyISAM 表count(*)很快 - 不支持事务;
- show table status 命令返回很快 - 不准确;
- InnoDB表之间count(*)会遍历全表 - 结果准确但会导致性能问题。
自己计数 - 找地方存操作记录表得行数
- 用缓存系统保存计数
更新很频繁的库 - 缓存系统来支持 - Redis服务来保存这个表的总行数 - 表每被插入一行Redis计数就加1,每被删除一行Redis计数就减1 - 读和更新操作都很快 -缓存系统可能会丢失更新
Redis 的数据不会永久地留在内存里 - 找地方将值定期持久化存储 - 仍然可能丢失更新【刚在数据表中插入了一行,Redis中保存的值也加1,Redis异常重启了】 - 数据库里单独执行一次count(*)获取真实行数 - 再把值写回到Redis里
计数保存在缓存系统中的方式,不只是丢失更新的问题。即使Redis正常工作,这个值在逻辑上不精确
- 在数据库保存计数
将这个计数直接放到数据库里单独的一张计数表C中
解决了崩溃问题【InnoDB 支持崩溃恢复不丢数据】
解决了值逻辑上不一致的问题
不同的count用法
-
返回满足条件的结果集的总行数 - count(*)、count(主键 id)、count(1)
-
返回满足条件的数据行里,该字段不为NULL的总个数 - count(字段)