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

相关推荐
万事大吉CC2 小时前
mysql单表查询·3
数据库·mysql
苹果醋38 小时前
大模型实战--FastChat一行代码实现部署和各个组件详解
java·运维·spring boot·mysql·nginx
计算机学姐10 小时前
基于SpringBoot+Vue的高校运动会管理系统
java·vue.js·spring boot·后端·mysql·intellij-idea·mybatis
-XWB-11 小时前
【MySQL】数据目录迁移
数据库·mysql
掘根12 小时前
【MySQL】Ubuntu环境下MySQL的安装与卸载
数据库·mysql·centos
知识分享小能手13 小时前
mysql学习教程,从入门到精通,SQL 修改表(ALTER TABLE 语句)(29)
大数据·开发语言·数据库·sql·学习·mysql·数据分析
fat house cat_13 小时前
mysql-索引笔记
数据库·mysql
He guolin13 小时前
【MySQL】数据库基础知识
数据库·mysql
F_D_Z15 小时前
【SQL】未订购的客户
数据库·sql·mysql
shylyly_17 小时前
Linux的基本指令(3)
数据库·mysql·linux的基本指令