Count的实现方式
- 2023-12-21 15:20:33
- 代婷婷
- 原创 585
在不同的mysql引擎中,count(*)有不同的实现方式。
MYISAM引擎把一个表的总行数存在了磁盘上,执行count(*)时直接返回,效率很高。如果加了where条件myisam也不会返回的这么快。
InnoDB执行count(*)时,把数据一行一行的从引擎里面读出来,然后累计计数。
InnoDB每一行记录都要判断是否对这个会话可见,所以只能一行一行的读出数据进行判断,可见的行才能计入当前这个查询的总行数。
InnoDB是组织索引表,主键索引树的叶子节点是数据,普通索引树的叶子节点是主键值。普通索引树比主键索引树小,count(*)遍历哪个索引树逻辑上是一样的,mysql优化器会找到最小的那棵树来遍历。保证逻辑正确的前提下,尽量减少扫描的数据量,是数据库系统设计的通用法则之一。
show table status 命令,输出结果里面也有一个TABLE_ROWS用于显示表当前有多少行,TABLE_ROWS是从采样估算来的,所以不是很准确,误差可能达到40%-50%,所以不能直接用TABLE_ROWS。
MyISAM引擎count(*)很快,但不支持事务;
Show table status 返回很快,但不准确;
InnoDB引擎直接count(*)会遍历全表,结果准确,会导致性能问题。
一、用缓存系统保存计数
更新很频繁的库,用缓存系统来支持,可以用一个redis服务来保存这个表的总行数,这个表每被插入一行,Redis计数就加一,每被删除一行Redis计数就减1,读和更新都会很快,但是缓存系统可能会丢失更新,比如Redis服务异常重启了
将计数保存到缓存系统中,可能会丢失更新,也会逻辑上不精确。
两种情况:
1. 查到的 100 行结果里面有最新插入记录,而 Redis 的计数里还没加 1;
2. 查到的 100 行结果里没有最新插入的记录,而 Redis 的计数里已经加了 1
二、 在数据库保存计数
利用事务,逻辑上可以保持一致,
会话A将计数值加1->会话B读计数值,取最近的100条数据->会话A插入一行数据
会话B执行的时候会话A还没有提交,所以对会话B是不可见的,逻辑上是一致的
按照效率排序的话,count(字段)