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

相关推荐
白衣鸽子23 分钟前
MySQL数据库的“隐形杀手”:深入理解文件结构与治理数据碎片
数据库·后端·mysql
java干货1 小时前
MySQL “灵异事件”:我 INSERT id=11,为什么被 UPDATE id=10 锁住了?
android·数据库·mysql
記億揺晃着的那天1 小时前
数据库中 ACID 四个核心特性
数据库·mysql·oracle·系统设计·acid
一抓掉一大把1 小时前
RuoYi .net-实现商城秒杀下单(redis,rabbitmq)
redis·mysql·c#·rabbitmq·.net
gx23482 小时前
MySQL-5-触发器和储存过程
android·mysql·adb
Zhao_yani3 小时前
Apache Drill 连接 MySQL 或 PostgreSQL 数据库
数据库·mysql·postgresql·drill
计算机学姐5 小时前
基于SpringBoot的高校社团管理系统【协同过滤推荐算法+数据可视化】
java·vue.js·spring boot·后端·mysql·信息可视化·推荐算法
learning-striving10 小时前
SQL server创建数据表
数据库·sql·mysql·sql server
切糕师学AI10 小时前
SQL中的函数索引/表达式索引
数据库·sql·mysql·postgresql·oracle
S_h_a_11 小时前
八股-Mysql 基础篇(1)
数据库·mysql