MySQL学习笔记-不同count统计的比较

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: MySQL学习笔记-不同count统计的比较

在做业务开发的时候,经常会使用 count() 函数对满足条件的结果集统计数量,但执行 select count(*) from t 这种语句的时候会很慢,而 count(*)、count(1)、count(字段)、count(主键id)这几种方式统计有所区别,这篇文章介绍学习一下不同 count() 之间区别。

1.笔记图

2.count(*) 的实现方式


  • MySQL 引擎MyISAM 引擎把一个表的总行数存在了磁盘上,因此执行 count(*) 的时候会直接返回这个数,效率很高(如果加了 where 条件的时候也需要扫描结果)
  • InnoDB 引擎InnoDB 执行 count(*) 的时候,需要把数据一行一行地从引擎里面读出来,然后累积计数
  • 为什么 InnoDB 不跟 MyISAM 一样,也把数字存起来呢?
  • 这是因为即使是在同一个时刻的多个查询,由于多版本并发控制(MVCC)的原因,InnoDB应该返回多少行 也是不确定的
  • 可重复读是它默认的隔离级别,在代码上就是 MVCC 来实现的。每一行记录都要判断自己是否对这个会话可见,对于 count(*) 请求来说,可见的行才能够用于计算 基于这个查询 的表的总行数

3.InnoDB count(*) 优化

  • 扫描行数选择
  • InnoDB 是索引组织表,主键索引树的叶子节点是数据,而普通索引树的叶子节点是主键值。所以,普通索引树比主键索引树小很多
  • MySQL 优化器会找到最小的那棵树来遍历,在保证逻辑正确的前提下,尽量减少扫描的数据量,是数据库系统设计的通用法则之一
  • TABLE_ROWS 能代替 count(*) 吗?
  • 索引扫描行数统计的值是通过采样来估算的,因此它也很不准
  • 官方文档说误差可能达到 40%50%。所以,show table status 命令显示的行数也不能直接使用。
  • 小结
  • MyISAM 表虽然 count(*) 很快,但是不支持事务
  • show table status 命令虽然返回很快,但是不准确
  • InnoDB 表直接 count(*) 会遍历全表,虽然结果准确,但会导致性能问题

4.count(*) 业务层优化

  • 用缓存系统保存计数
  • 使用 Redis 记录总数:你可以用一个 Redis 服务来保存这个表的总行数。这个表每被插入一行 Redis 计数就加 1,每被删除一行 Redis 计数就减 1
  • 使用 Redis 记录总数带来的问题
  • 缓存系统可能会丢失更新
  • 试想如果刚刚在数据表中插入了一行,Redis 中保存的值也加了 1,然后 Redis 异常重启了,重启后你要从存储 redis 数据的地方把这个值读回来,而刚刚加 1 的这个计数操作却丢失了
  • 将计数保存在缓存系统中的方式,还不只是丢失更新的问题。即使 Redis 正常工作,这个值还是逻辑上不精确的
  • 在数据库保存计数:计数直接放到单独的一张计数表中
  • 这里要解决的问题是由于 InnoDB 要支持事务,从而导致 InnoDB 表不能把 count(*) 直接存起来,然后查询的时候直接返回形成的
  • 以子之矛攻子之盾,利用 事务 这个特性,可以把问题解决掉

5.InnoDB 不同的 count 用法

  • count( ) 含义count() 是一个聚合函数,对于返回的结果集,一行行地判断,如果 count 函数的参数不是 NULL,累计值就加 1,否则不加。最后返回累计值。
  • 分析性能差别原则
  • server 层要什么就给什么
  • InnoDB 只给必要的值
  • 优化器只优化了 count(*) 的语义为“取行数”,其他显而易见的优化并没有做
  • count(*):并不会把全部字段取出来,而是专门做了优化,不取值。count(*) 肯定不是 null,按行累加
  • count(主键 id)InnoDB 引擎会遍历整张表,把每一行的 id 值都取出来,返回给 server 层。server 层拿到 id 后,判断是不为空的,就按行累加
  • count(字段)
  • 如果这个字段是定义为 not null 的话,一行行地从记录里面读出这个字段,判断不能为 null,按行累加
  • 如果这个字段定义允许为 null,那么执行的时候,判断到有可能是 null,还要把值取出来再判断一下,不是 null 才累加
  • count(1)InnoDB 引擎遍历整张表,但不取值。server 层对于返回的每一行,放一个数字1进去,判断是不可能为空的,按行累加。
  • 按照效率排序:count(字段)<count(主键 id)<count(1)≈count(*),所以建议你,尽量使用 count(*)

相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
6天前
|
存储 SQL 关系型数据库
轻松入门MySQL:加速进销存!利用MySQL存储过程轻松优化每日销售统计(15)
轻松入门MySQL:加速进销存!利用MySQL存储过程轻松优化每日销售统计(15)
|
6天前
|
存储 关系型数据库 MySQL
Linux C/C++ 开发(学习笔记八):Mysql数据库图片存储
Linux C/C++ 开发(学习笔记八):Mysql数据库图片存储
58 0
|
6天前
|
关系型数据库 MySQL 数据库
Linux C/C++ 开发(学习笔记七):Mysql数据库C/C++编程实现 插入/读取/删除
Linux C/C++ 开发(学习笔记七):Mysql数据库C/C++编程实现 插入/读取/删除
57 0
|
6天前
|
SQL 关系型数据库 MySQL
mysql一条sql查询出多个统计结果
mysql一条sql查询出多个统计结果
17 0
|
6天前
|
关系型数据库 MySQL
MySQL学习笔记
MySQL学习笔记
|
6天前
|
安全 关系型数据库 MySQL
某教程学习笔记(一):09、MYSQL数据库漏洞
某教程学习笔记(一):09、MYSQL数据库漏洞
22 0
|
6天前
|
存储 关系型数据库 MySQL
《高性能Mysql》学习笔记(二)
《高性能Mysql》学习笔记(二)
138 0
|
6天前
|
存储 SQL 关系型数据库
《高性能Mysql》学习笔记(一)
《高性能Mysql》学习笔记(一)
97 0
|
6天前
|
关系型数据库 MySQL 数据库
『 MySQL数据库 』聚合统计
『 MySQL数据库 』聚合统计
|
6天前
|
关系型数据库 MySQL Linux
Linux C/C++ 开发(学习笔记六):MySQL安装与远程连接
Linux C/C++ 开发(学习笔记六):MySQL安装与远程连接
53 0