MySQL 中的count(*) 与 count(1) 谁更快一些?

目录
  • 1.实践
  • 2.explain分析
  • 3.原理分析
    • 3.1主键索引与普通索引
    • 3.2原理分析
  • 4.MyISAM呢?

先说结论:这两个性能差别不大。

1.实践

我准备了一张有 100W 条数据的表,表结构如下:

CREATE TABLE `user` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `username` varchar(255) DEFAULT NULL,
  `address` varchar(255) DEFAULT NULL,
  `password` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

可以看到,有一个主键索引。我们来用两种方式统计一下表中的记录数,如下:

可以看到,两条 SQL 的执行效率其实差不多,都是 0.14s。

再来看另外两个统计:

id 是主键,username 以及 address 则是普通字段。可以看出,用 id 来统计,也有一丢丢优势。松哥这里因为测试数据样板比较小,所以效果不明显,小伙伴们可以加大测试数据量,那么这种差异会更加明显。

那么到底是什么原因造成的这种差异,接下来我们就来简单分析一下。

2. explain 分析

我们先用 explain 来看下这几个 SQL 不同的执行计划:

可以看到,前三个统计方式的执行计划是一样的,后面两个是一样的。我这里和大家比较下 explain 中的不同项:

  • type:前三个的 type 值为 index,表示全索引扫描,就是把整个索引过一遍就行(注意是索引不是整个表);后两个的 type 值为 all,表示全表扫描,即不会使用索引。
  • key:这个表示 MySQL 决定采用哪个索引来优化对该表的访问,PRIMARY 表示利用主键索引,NULL 表示不用索引。
  • key_len:这个表示 MySQL 使用的键长度,因为我们的主键类型是 INT 且非空,所以值为 4。
  • Extra:这个中的 Using index 表示优化器只需要通过访问索引就可以获取到需要的数据(不需要回表)。

通过 explain 我们其实也能大概看出来前三种统计方式的执行效率是要高一些的(因为用到了索引),而后面两种的统计效率相对来说要低一些的(没用索引,需要全表扫描)。仅有上面的分析还不够,我们再来从原理角度来分析一下。

3. 原理分析

3.1 主键索引与普通索引

在开始原理分析以前,我想先带领大家看一下 B+ 树,这对于我们理解接下来的内容有重要作用。大家都知道,InnoDB 中索引的存储结构都是 B+ 树(至于什么是 B+ 树,和 B 树有什么区别,这个本文就不讨论了,这两个单独都能整出来一篇文章),主键索引和普通索引的存储又有所不同,

如下图表示主键索引:

可以看到,在主键索引中,叶子结点保存了每一行的数据。而在普通索引中,叶子结点保存的是主键值,当我们使用普通索引去搜索数据的时候,先在叶子结点中找到主键,再拿着主键去主键索引中查找数据,相当于做了两次查找,这也就是我们平常所说的回表操作。

3.2 原理分析

不知道小伙伴们有没有注意过,我们学习 MySQL 的时候,count 函数是归在聚合函数那一类的,就是 avg、sum 等,count 函数和这些归在一起,说明它也是一个聚合函数。既然是聚合函数,那么就需要对返回的结果集进行一行行的判断,这里就涉及到一个问题,返回的结果是啥?我们分别来看:对于​ ​​select count(1) from user;​​ ​这个查询来说,InnoDB 引擎会去找到一个最小的索引树去遍历(不一定是主键索引),但是不会读取数据,而是读到一个叶子节点,就返回 1,最后将结果累加。对于​ ​​select count(id) from user;​​ ​ 这个查询来说,InnoDB 引擎会遍历整个主键索引,然后读取 id 并返回,不过因为 id 是主键,就在 B+ 树的叶子节点上,所以这个过程不会涉及到随机 IO(并不需要回表等操作去数据页拿数据),性能也是 OK 的。对于​​​select count(username) from user;​​ ​ 这个查询来说,InnoDB 引擎会遍历整张表做全表扫描,读取每一行的 username 字段并返回,如果 username 在定义时候设置了 not null,那么直接统计 username 的个数;如果 username 在定义的时候没有设置 not null,那么就先判断一下 username 是否为空,然后再统计。最后再来说说​ ​​select count(*) from user;​​ ​,这个 SQL 的特殊之处在于它被 MySQL 优化过,当 MySQL 看到 ​​count(*)​​ ​就知道你是想统计总记录数,就会去找到一个最小的索引树去遍历,然后统计记录数。因为主键索引(聚集索引)的叶子节点是数据,而普通索引的叶子节点则是主键值,所以普通索引的索引树要小一些。然而在上文的案例中,我们只有主键索引,所以最终使用的就是主键索引。现在,如果我修改上面的表,为 username 字段也添加索引,然后我们再来看​ ​​explain select count(*) from user;​​ ​的执行计划:

可以看到,此时使用的索引就是 username 索引了,和我们前面的分析结果是一致的。从上面的描述中我们就可以看出,第一个查询性能最高,第二个次之(因为需要读取 id 并返回),第三个最差(因为需要全表扫描),第四个的查询性能则接近第一个。

4. MyISAM 呢?

可能有小伙伴知道,MyISAM 引擎中的​ ​​select count(*) from user;​​ ​操作执行起来是非常快的,那是因为 MyISAM 把表中的行数直接存在磁盘中了,需要的时候直接读取出来就行了,所以非常快。MyISAM 引擎之所以这样做,主要是因为它是不支持事务的,所以它的统计实际上就非常容易,添加一行记录一行就行了。而我们常用的 InnoDB 却不能这样做!为啥?因为 InnoDB 支持事务!为了支持事务,InnoDB 引入了 MVCC 多版本并发控制,所以在数据读取的时候可能会有脏读、幻读以及不可重复读等问题。所以,InnoDB 需要将每一行数据拿出来,判断该行数据对当前会话是否可见,如果可见,就统计该行数据,否则不予统计。

到此这篇关于MySQL 中 count(*) 与 count(1) 谁更快一些?的文章就介绍到这了,更多相关MySQL 中 count(*) 与 count(1) 内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • MySQL中count(*)、count(1)和count(col)的区别汇总

    前言 count函数是用来统计表中或数组中记录的一个函数,count(*) 它返回检索行的数目, 不论其是否包含 NULL值.最近感觉大家都在讨论count的区别,那么我也写下吧:欢迎留言讨论,话不多说了,来一起看看详细的介绍吧. 1.表结构: dba_jingjing@3306>[rds_test]>CREATE TABLE `test_count` ( -> `c1` varchar(10) DEFAULT NULL, -> `c2` varchar(10) DEFAULT N

  • MySQL 中的count(*) 与 count(1) 谁更快一些?

    目录 1.实践 2.explain分析 3.原理分析 3.1主键索引与普通索引 3.2原理分析 4.MyISAM呢? 先说结论:这两个性能差别不大. 1.实践 我准备了一张有 100W 条数据的表,表结构如下: CREATE TABLE `user` (   `id` int(11) unsigned NOT NULL AUTO_INCREMENT,   `username` varchar(255) DEFAULT NULL,   `address` varchar(255) DEFAULT

  • MySQL count(1)、count(*)、count(字段)的区别

    目录 1.初识COUNT 2.COUNT(字段).COUNT(常量)和COUNT(*)之间的区别 3.COUNT(*)的优化 MyISAM InnoDB 4.COUNT(*)和COUNT(1) 5.COUNT(字段) 6.总结 关于数据库中行数统计,无论是MySQL还是Oracle,都有一个函数可以使用,那就是COUNT. 但是,就是这个常用的COUNT函数,却暗藏着很多玄机,尤其是在面试的时候,一不小心就会被虐.不信的话请尝试回答下以下问题: > 1.COUNT有几种用法? > 2.COUN

  • MySQL中存储的数据查询的时候如何区分大小写

    目录 场景描述 解决办法 1. 查询时指定大小写敏感 2. 定义表结构时指定字段大小写敏感 3. 修改排序规则(COLLATE) COLLATE是用来做什么的? 各种COLLATE的区别 COLLATE 设置级别及其优先级 总结 场景描述 今天在将 Hive 表同步到 MySQL 之后,其中有一列是唯一列,但是在 MySQL 中查询的时候 count 与 distinct count 查询出来的数值是不一样的,这么来看的话是有重复的数据(按理说不应该的,因为在 Hive 中,这两个数值是一样的)

  • MySQL中distinct和count(*)的使用方法比较

    首先对于MySQL的DISTINCT的关键字的一些用法: 1.在count 不重复的记录的时候能用到,比如SELECT COUNT( DISTINCT id ) FROM tablename:就是计算talbebname表中id不同的记录有多少条. 2,在需要返回记录不同的id的具体值的时候可以用,比如SELECT DISTINCT id FROM tablename:返回talbebname表中不同的id的具体的值. 3.上面的情况2对于需要返回mysql表中2列以上的结果时会有歧义,比如SE

  • MySQL中聚合函数count的使用和性能优化技巧

    本文的环境是Windows 10,MySQL版本是5.7.12-log 一. 基本使用 count的基本作用是有两个: 统计某个列的数据的数量: 统计结果集的行数: 用来获取满足条件的数据的数量.但是其中有一些与使用中印象不同的情况,比如当count作用一列.多列.以及使用*来表达整行产生的效果是不同的. 示例表如下: CREATE TABLE `NewTable` ( `id` int(11) NULL DEFAULT NULL , `name` varchar(30) NULL DEFAUL

  • MySQL中无过滤条件的count详解

    count(*) 实现 1.MyISAM:将表的总行数存放在磁盘上,针对无过滤条件的查询可以直接返回 如果有过滤条件的count(*),MyISAM也不能很快返回 2.InnoDB:从存储引擎一行行地读出数据,然后累加计数 由于MVCC,在同一时刻,InnoDB应该返回多少行是不确定 样例 假设表t有10000条记录 session A session B session C BEGIN; SELECT COUNT(*) FROM t;(返回10000) INSERT INTO t;(插入一行)

  • 详解 MySQL中count函数的正确使用方法

    1. 描述 在MySQL中,当我们需要获取某张表中的总行数时,一般会选择使用下面的语句 select count(*) from table; 其实count函数中除了*还可以放其他参数,比如常数.主键id.字段,那么它们有什么区别?各自效率如何?我们应该使用哪种方式来获取表的行数呢? 当搞清楚count函数的运行原理后,相信上面几个问题的答案就会了然于胸. 2. 表结构 为了解决上述的问题,我创建了一张 user 表,它有两个字段:主键id和name,后者可以为null,建表语句如下. CRE

  • MySQL中count(*)执行慢的解决方案

    目录 一. count(*) 的实现方式 1.实现方式比较 2.为什么InnoDB不像MyISAM一样,也把数字存起来 3.小结 二.计数方法 1.用缓存系统保存计数 2.在数据库保存计数 三.不同的 count 用法 1. count(主键 id) 2.count(1) 3.count(字段) 4.count(*) 前言: 在开发工作中,经常需要计算一个表的行数,比如一个内容系统审核记录总数.这时候我们最先想到是一条 select count(*) from my_table;语句.但是,随着

  • Mysql中count(*)、count(1)、count(主键id)与count(字段)的区别

    目录 count()函数 count(*).count(1) .count(主键id) 和 count(字段) 区别 count(主键id) 与 count(1) count(字段) count(非空字段) count(可空字段) count(*) 执行效率 执行效果上: 执行效率上: 实例分析 count()函数 count() 是一个聚合函数,对于返回的结果集,一行行地判断,如果 count 函数的参数不是 NULL,累计值就加 1,否则不加.最后返回累计值. count(*).count(

  • Mysql中使用count加条件统计的实现示例

    目录 前言 测试环境 准备工作 条件统计 总结 前言 最近发现在处理Mysql问题时,count()函数频繁上镜,常常出现在分组统计的情景下,但是有时候并不是使用group by分好组就可以直接统计了,比如说一个常见的需求,统计每个班级男生所占的比例,这种情况一般会按照班级分组,但是分组内不但要统计班级的人数,还要统计男生的人数,也就是说统计是有条件的,之前确实没有考虑过怎样实心,后来查询了资料,总结在这里,方便日后查找使用. Mysql中count()函数的一般用法是统计字段非空的记录数,所以

随机推荐