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 的行为。

相关推荐
消失在人海中2 小时前
oracle与MySQL数据库之间数据同步的技术要点
数据库·mysql·oracle
镜花水月602 小时前
🐬 MySQL 慢查询分析与 InnoDB 参数优化实战:以 io_global_by_wait_by_latency 为例
mysql
xrkhy3 小时前
Linux系统的CentOS7发行版安装MySQL80
linux·mysql·centos
Johny_Zhao3 小时前
基于CentOS Stream 8的物联网数据采集与展示方案
linux·网络·python·mqtt·mysql·网络安全·信息安全·云计算·shell·yum源·系统运维
2401_831501734 小时前
MySQL 知识小结(一)
数据库·mysql
不爱搬砖的码农4 小时前
windows系统MySQL安装文档
windows·mysql·adb
23级二本计科5 小时前
14.MySQL用C语言连接
数据库·mysql
运维成长记5 小时前
Zabbix 高可用架构部署方案(2最新版)
mysql·架构·zabbix
琪阿不会编程5 小时前
Mysql8 忘记密码重置,以及问题解决
android·数据库·sql·mysql
955.5 小时前
tomcat+mysql电商实战项目毕业设计
mysql·tomcat·课程设计