mysql 获取行数的n种方式详解

前言

在开发中,经常很多用在统计表的总数据时,有用count(*),count(1) 还有count(字段)。这篇文章我将详细讲解他们之间有什么不同,他们的底层是怎么实现的。

你会发现一个现象随着记录越来越多,select count(*) from t_user ,这条语句执行也会越来越慢。你惊奇的发现,就这条语句而已myisam 比 innodb 要快,为什么呢,就拿innodb 而言,这条语句第一次查询比第二次查询要慢很多,又是为什么呢?下面关于这些问题我都会给大家详细解决。

count(*) 如何实现的

对于查询一个表有多少行时,就是像这样的语句 select count(*) from t_user ,不同的引擎来说,他们的实现方式不一样。

对于Myisam 来说,会把一个表的总行数存在磁盘,在查询一个表有多少行数据时,直接返回这个数,效率很高。 但是Innodb 就没有那么幸运了,是innodb 一行一行的读的,server 层在累积计数的。

大家是不是感觉innodb 很傻,为啥不也把总行数写在磁盘呢?不是傻,它这么做也是有苦衷的,不得不这么干。innodb 有个mvcc 的概念,也就是一个数据有多个版本,查询是当前读,它必须根据当前事务,然后通过视图,计算每行数据的可见性,如果这条数据可见,innodb 就返回server 层累积计数。

如果有where 条件,Myisam 也是需要根据索引返回每行数据,server 累积计算,效率就没有那么高,innodb 还是向上面一样去统计,但是innodb要判断数据的可见性,感觉效率低于Myisam,这是我个人的看法。

有人说如果计算数据的总行数,能不能存下来,当然可以呀,这样直接读出来很快的呀。

用什么东西存,建议还是当前库的当前表,这样一个insert 和 update 更新总行数就是一个事物,根据事物的特性,保证数据的一致性也就说 insert 和 update 能保证数据的一致。

对于其他的呢,特别是跨数据库,比如说用redis 存数据总数,不管怎么存都是有用问题的,redis incr 和 mysql insert 没办法保证他们的原子性。要么就是数据总行数已经加一了数据还没有存入库中,要么已经存入库了,数据总行数没有加一。如果真的想用Redis ,只能用分布式锁,客户端的操作都变成了串行,分布式锁会多一次网络开销,性能低于mysql 本地事物。那么对于其他的分布式事务呢,redis 不支持2pc 协议,当然与2pc 相关的都不能用。只有saga ,cdc,事务消息 等借助消息队列。能实现redis 和 mysql 数据的最终一致性,也只能是最终一致性,这个和消费者消费能力,消息队列负载都有关系,不是原子性的操作,一定是有问题的。

我们在提到索引时,经常会用explain 查看扫描的行数也就是row,这个row 和 show table status 一样的。能不能用这个命令呢,是不可以的,这两个命令获取的row 不准确,它们只是通过取样统计,计算大概行数,为优化器用什么索引提供理论依据,说白了就是选择索引用的,大概算法是 取出n页数据,算出有m个不同的值,再通过(m/n)*总页数 就算出了这个行数。

列是 not null count(*),count(1) 和 count(字段)

count(*), innodb 会选择一个最小的索引树找到叶子节点逐行扫描,放回server 层累加。

count(1) innodb 会选择一个最小的索引树找到叶子节点逐行扫描,放到 server 层 放一个数字"1"进去,按行累加。

count(字段) nnodb 会选择一个最小的索引树找到逐行扫描,解析id 返回 server 层累加。

列是 null count(*),count(1) 和 count(字段)

count(*), innodb 会选择一个最小的索引树找到叶子节点逐行扫描,放回server 层累加。

count(1) innodb 会选择一个最小的索引树找到叶子节点逐行扫描,放到 server 层 放一个数字"1"进去,判断不为空按行累加。

count(字段) nnodb 会选择一个最小的索引树找到逐行扫描,解析字段,判断不为null ,按行累加。

最后我在解释下同样条件第二次为啥比第一次快,这是因为会把结果存在 buffer pool 里,直接从这里面取。

综上所述,我们可以看到就效率而言 count(字段)<count(主键 id)<count(1)≈count(*),所以我建议你,尽量使用 count(*)。

我们还可以看到引擎只负责取server 要的数据,累加是server 的行为。

相关推荐
a努力。1 小时前
国家电网Java面试被问:混沌工程在分布式系统中的应用
java·开发语言·数据库·git·mysql·面试·职场和发展
Mr__Miss4 小时前
说下Mysql的MVCC机制
数据库·mysql
酉鬼女又兒6 小时前
SQL24 统计每个用户的平均刷题数
数据库·sql·mysql
一只自律的鸡7 小时前
【MySQL】第六章 子查询
数据库·mysql
weixin_462446239 小时前
一键安装 MySQL 5.7(CentOS 7)自动化脚本详解
mysql·centos·自动化
陌上丨11 小时前
MySQL8.0高可用集群架构实战
数据库·mysql·架构
一只自律的鸡12 小时前
【MySQL】第十一章 存储过程和存储函数
数据库·mysql
翔云12345612 小时前
MySQL 中的 utf8 vs utf8mb4 区别
数据库·mysql
强子感冒了12 小时前
MySQL学习随笔:数据类型与字段约束
学习·mysql
一只酸奶牛^_^12 小时前
解决LinuxDeploy部署mysql、redis数据库无法启动问题。
redis·mysql