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

相关推荐
J_bean8 小时前
剖析 MySQL InnoDB 共享行锁 (S) & 排他行锁 (X)
数据库·mysql·共享行锁·s lock·排他行锁·x lock
技术长镜头8 小时前
别再只说“走 B+Tree”:一条 SQL 在 InnoDB 中的完整寻址过程
后端·mysql
一只fish1 天前
优化器架构对比:Oracle vs PostgreSQL vs MySQL
mysql·postgresql·oracle
buyue__1 天前
Python实现连接MySQL数据库并执行目录下的所有SQL文件,遇到错误时终止执行。
python·mysql
snow@li1 天前
MySQL:库表设计完整规范与实战方案
数据库·mysql
Shawn Dev1 天前
MySQL Binlog 数据误删除恢复完全指南
数据库·mysql·elasticsearch
陈卿诺语1 天前
MySQL错误-this is incompatible with sql_mode=only_full_group_by完美解决方案
sql·mysql·adb
南城以南溫暖如初1471 天前
社区健身小程序开发流程详解:技术选型与实施经验分享
java·经验分享·spring boot·mysql·vue·apache
凤舞飘伶1 天前
导出数据库表结构excel
python·mysql
️学习的小王1 天前
MySQL常用函数知识点总结
数据库·mysql·oracle