MySQL 中 count(*) 和 count(1) 有什么区别?哪个性能最好?

20 篇文章 119 订阅
订阅专栏

大家好,我是小林。

当我们对一张数据表中的记录进行统计的时候,习惯都会使用 count 函数来统计,但是 count 函数传入的参数有很多种,比如 count(1)、count(*)、count(字段) 等。

到底哪种效率是最好的呢?是不是 count(*) 效率最差?

我曾经以为 count(*) 是效率最差的,因为认知上 selete * from t 会读取所有表中的字段,所以凡事带有 * 字符的就觉得会读取表中所有的字段,当时网上有很多博客也这么说。

但是,当我深入 count 函数的原理后,被啪啪啪的打脸了!

不多说, 发车!

图片

哪种 count 性能最好?

哪种 count 性能最好?

我先直接说结论:

图片

要弄明白这个,我们得要深入 count 的原理,以下内容基于常用的 innodb 存储引擎来说明。

count() 是什么?

count() 是一个聚合函数,函数的参数不仅可以是字段名,也可以是其他任意表达式,该函数作用是统计符合查询条件的记录中,函数指定的参数不为 NULL 的记录有多少个

假设 count() 函数的参数是字段名,如下:

select count(name) from t_order;

这条语句是统计「 t_order 表中,name 字段不为 NULL 的记录」有多少个。也就是说,如果某一条记录中的 name 字段的值为 NULL,则就不会被统计进去。

再来假设 count() 函数的参数是数字 1 这个表达式,如下:

select count(1) from t_order;

这条语句是统计「 t_order 表中,1 这个表达式不为 NULL 的记录」有多少个。

1 这个表达式就是单纯数字,它永远都不是 NULL,所以上面这条语句,其实是在统计 t_order 表中有多少个记录。

count(主键字段) 执行过程是怎样的?

在通过 count 函数统计有多少个记录时,MySQL 的 server 层会维护一个名叫 count 的变量。

server 层会循环向 InnoDB 读取一条记录,如果 count 函数指定的参数不为 NULL,那么就会将变量 count 加 1,直到符合查询的全部记录被读完,就退出循环。最后将 count 变量的值发送给客户端。

InnoDB 是通过 B+ 树来保持记录的,根据索引的类型又分为聚簇索引和二级索引,它们区别在于,聚簇索引的叶子节点存放的是实际数据,而二级索引的叶子节点存放的是主键值,而不是实际数据。

用下面这条语句作为例子:

//id 为主键值
select count(id) from t_order;

如果表里只有主键索引,没有二级索引时,那么,InnoDB 循环遍历聚簇索引,将读取到的记录返回给 server 层,然后读取记录中的 id 值,就会 id 值判断是否为 NULL,如果不为 NULL,就将 count 变量加 1。

图片

但是,如果表里有二级索引时,InnoDB 循环遍历的对象就不是聚簇索引,而是二级索引。

图片

这是因为相同数量的二级索引记录可以比聚簇索引记录占用更少的存储空间,所以二级索引树比聚簇索引树小,这样遍历二级索引的 I/O 成本比遍历聚簇索引的 I/O 成本小,因此「优化器」优先选择的是二级索引。

count(1) 执行过程是怎样的?

用下面这条语句作为例子:

select count(1) from t_order;

如果表里只有主键索引,没有二级索引时。

图片

那么,InnoDB 循环遍历聚簇索引(主键索引),将读取到的记录返回给 server 层,但是不会读取记录中的任何字段的值,因为 count 函数的参数是 1,不是字段,所以不需要读取记录中的字段值。参数 1 很明显并不是 NULL,因此 server 层每从 InnoDB 读取到一条记录,就将 count 变量加 1。

可以看到,count(1) 相比 count(主键字段) 少一个步骤,就是不需要读取记录中的字段值,所以通常会说 count(1) 执行效率会比 count(主键字段) 高一点。

但是,如果表里有二级索引时,InnoDB 循环遍历的对象就二级索引了。

图片

count(*) 执行过程是怎样的?

看到 * 这个字符的时候,是不是大家觉得是读取记录中的所有字段值?

对于 selete * 这条语句来说是这个意思,但是在 count(*) 中并不是这个意思。

count(\*) 其实等于 count(0),也就是说,当你使用 count(*) 时,MySQL 会将 * 参数转化为参数 0 来处理。

图片

所以,count(*) 执行过程跟 count(1) 执行过程基本一样的,性能没有什么差异。

在 MySQL 5.7 的官方手册中有这么一句话:

InnoDB handles SELECT COUNT(\*) and SELECT COUNT(1) operations in the same way. There is no performance difference.

翻译:InnoDB以相同的方式处理SELECT COUNT(\*)和SELECT COUNT(1)操作,没有性能差异。

而且 MySQL 会对 count(*) 和 count(1) 有个优化,如果有多个二级索引的时候,优化器会使用key_len 最小的二级索引进行扫描。

只有当没有二级索引的时候,才会采用主键索引来进行统计。

count(字段) 执行过程是怎样的?

count(字段) 的执行效率相比前面的 count(1)、 count(*)、 count(主键字段) 执行效率是最差的。

用下面这条语句作为例子:

//name不是索引,普通字段
select count(name) from t_order;

对于这个查询来说,会采用全表扫描的方式来计数,所以它的执行效率是比较差的。

图片

小结

count(1)、 count(*)、 count(主键字段)在执行的时候,如果表里存在二级索引,优化器就会选择二级索引进行扫描。

所以,如果要执行 count(1)、 count(*)、 count(主键字段) 时,尽量在数据表上建立二级索引,这样优化器会自动采用 key_len 最小的二级索引进行扫描,相比于扫描主键索引效率会高一些。

再来,就是不要使用 count(字段) 来统计记录个数,因为它的效率是最差的,会采用全表扫描的方式来统计。如果你非要统计表中该字段不为 NULL 的记录个数,建议给这个字段建立一个二级索引。

为什么要通过遍历的方式来计数?

你可以会好奇,为什么 count 函数需要通过遍历的方式来统计记录个数?

我前面将的案例都是基于 Innodb 存储引擎来说明的,但是在 MyISAM 存储引擎里,执行 count 函数的方式是不一样的,通常在没有任何查询条件下的 count(*),MyISAM 的查询速度要明显快于 InnoDB。

使用 MyISAM 引擎时,执行 count 函数只需要 O(1 )复杂度,这是因为每张 MyISAM 的数据表都有一个 meta 信息有存储了row_count值,由表级锁保证一致性,所以直接读取 row_count 值就是 count 函数的执行结果。

而 InnoDB 存储引擎是支持事务的,同一个时刻的多个查询,由于多版本并发控制(MVCC)的原因,InnoDB 表“应该返回多少行”也是不确定的,所以无法像 MyISAM一样,只维护一个 row_count 变量。

举个例子,假设表 t_order 有 100 条记录,现在有两个会话并行以下语句:

图片

在会话 A 和会话 B的最后一个时刻,同时查表 t_order 的记录总个数,可以发现,显示的结果是不一样的。所以,在使用 InnoDB 存储引擎时,就需要扫描表来统计具体的记录。

而当带上 where 条件语句之后,MyISAM 跟 InnoDB 就没有区别了,它们都需要扫描表来进行记录个数的统计。

如何优化 count(*)?

如果对一张大表经常用 count(*) 来做统计,其实是很不好的。

比如下面我这个案例,表 t_order 共有 1200+ 万条记录,我也创建了二级索引,但是执行一次 select count(*) from t_order 要花费差不多 5 秒!

图片

面对大表的记录统计,我们有没有什么其他更好的办法呢?

*第一种,近似值*

如果你的业务对于统计个数不需要很精确,比如搜索引擎在搜索关键词的时候,给出的搜索结果条数是一个大概值。

图片

这时,我们就可以使用 show table status 或者 explain 命令来表进行估算。

执行 explain 命令效率是很高的,因为它并不会真正的去查询,下图中的 rows 字段值就是 explain 命令对表 t_order 记录的估算值。

图片

第二种,额外表保存计数值

如果是想精确的获取表的记录总数,我们可以将这个计数值保存到单独的一张计数表中。

当我们在数据表插入一条记录的同时,将计数表中的计数字段 + 1。也就是说,在新增和删除操作时,我们需要额外维护这个计数表。

MySQL进阶】count(*) 、count(1)、count(字段)到底有什么区别?哪种性能好?
blblccc
12-30 338
MySQL进阶】count(*) 、count(1)、count(字段)到底有什么区别?哪种性能好?
MySQLcount(*)、count(1)和count(col)的区别汇总
12-16
前言 count函数是用来统计表或数组记录的一个函数,count(*) 它返回检索行的数目, 不论其是否包含 NULL值。最近感觉大家都在讨论count区别,那么我也写下吧:欢迎留言讨论,话不多说了,来一起看看详细的介绍吧。 1、表结构: dba_jingjing@3306>[rds_test]>CREATE TABLE `test_count` ( -> `c1` varchar(10) DEFAULT NULL, -> `c2` varchar(10) DEFAULT NULL, -> KEY `idx_c1` (`c1`) -> ) ENGINE=InnoDB DEFA
count(1) count(* ) count(字段) 的区别
最新发布
刘皇叔说Java的博客
08-30 746
COUNT(1)和COUNT(*): 统计所有行的数量,无论行内数据是否为NULL。COUNT(字段): 只统计特定字段非NULL的行数量。在实际使用COUNT(*)是最常见的选择,适合大多数场景。COUNT(字段)则在需要排除NULL值时非常有用。
Select count(*)和Count(1)的区别和执行方式
weixin_33726943的博客
02-10 1874
    在SQL ServerCount(*)或者Count(1)或者Count([列])或许是最常用的聚合函数。很多人其实对这三者之间是区分不清的。本文会阐述这三者的作用,关系以及背后的原理。     往常我经常会看到一些所谓的优化建议不使用Count(* )而是使用Count(1),从而可以提升性能,给出的理由是Count( *)会带来全表扫描。而实际上如何写Count并没有区别。   ...
count(1)、count(*)与count(列名)的执行区别
qq_41853758的博客
12-11 4683
执行效果: 1.  count(1) and count(*) 当表的数据量大些时,对表作分析之后,使用count(1)还要比使用count(*)用时多了!  从执行计划来看,count(1)和count(*)的效果是一样的。 但是在表做过分析之后,count(1)会比count(*)的用时少些(1w以内数据量),不过差不了多少。    如果count(1)是聚索引,id,那肯定是count...
count(*)和count(1)和count(列名)的区别
热门推荐
XVII 的博客
11-23 3万+
count是一种最简单的聚合函数,一般也是我们第一个开始学习的聚合函数,那么他们之间究竟由什么区别呢? 有的人说count(1)和count(*)他们之间有区别,而有的人说他们之间没有区别那么他们之间到底有没有区别呢。 从执行结果来说: count(1)和count(*)之间没有区别,因为count(*)count(1)都不会去过滤空值, 但count(列...
转载:count(*)和count(1)的区别
黄道婆的专栏
11-08 1645
原始链接:https://blog.csdn.net/weixin_43980049/article/details/89327782 count(*)和count(1)的区别是什么? weixin_439800492019-04-16 10:42:124682收藏10 分类专栏:sql文章标签:count(*)和count(1)的区别 版权 个人总结如下: 一、count(*)和count(1)查询速度 使用count函数,当要统计的数量比较大时,发现count(*)花...
MySQL count() 和 count(1) 有什么区别?哪个性能最好?
m0_52789121的博客
08-02 187
大家好,我是小林。当我们对一张数据表的记录进行统计的时候,习惯都会使用count函数来统计,但是count函数传入的参数有很多种,比如count(1)、count(*)、count(字段)等。到底哪种效率是最好的呢?是不是count(*)效率最差?我曾经以为count(*)是效率最差的,因为认知上会读取所有表的字段,所以凡事带有*字符的就觉得会读取表所有的字段,当时网上有很多博客也这么说。但是,当我深入count函数的原理后,被啪啪啪的打脸了!不多说,发车!...
聊聊MySQLCOUNT(*)的性能
01-19
基本职场上的程序员用来统计数据库表的行数都会使用count(*),count(1)或者count(主键),那么它们之间的区别性能你又是否了解呢? 其实程序员在开发的过程,在一张大表上统计总行数是非常耗时的一个操作,那么...
count(*) ,count(字段)和 count(1) 有什么本质区别?哪个性能最好?
yonggeit的博客
07-27 40
如果表里只有主键索引,没有二级索引时,那么,InnoDB 循环遍历聚簇索引,将读取到的记录返回给 server 层,然后读取记录的 id 值,就会 id 值判断是否为 NULL,如果不为 NULL,就将 count 变量加 1。,因为 count 函数的参数是 1,不是字段,所以不需要读取记录的字段值。InnoDB 是通过 B+ 树来保存记录的,根据索引的类型又分为聚簇索引和二级索引,它们区别在于,聚簇索引的叶子节点存放的是实际数据,而二级索引的叶子节点存放的是主键值,而不是实际数据。
count(*)、count(1)和count(列名)的区别
weixin_33739627的博客
11-19 1367
count(*)、count(1)和count(列名)的区别   1、执行效果上:   l  count(*)包括了所有的列,相当于行数,在统计结果的时候,不会忽略列值为NULL   l  count(1)包括了忽略所有列,用1代表代码行,在统计结果的时候,不会忽略列值为NULL   l  count(列名)只包括列名那一列,在统计结果的时候,会忽略列值为空(这里的空不是只空字符串或者0...
count(*)与count(1)的区别
weixin_40561490的博客
05-13 618
一般来说, 百度得到的结果都会这样告诉你 count(1)和count(*)之间没有区别,因为count(*)count(1)都不会去过滤空值, 但count(列名)就有区别了,因为count(列名)会去过滤空值。 那么可以得到 count(id) > count(*) = count(1) > count(列名),这里假设id为主键。 好,下面来试验一下。 准备一个简单表,数据量为 999999 分别多次执行统计语句 SELECT COUNT(id) FROM t_or
面试百问之count(1) 和 count(*) 区别是什么?
weixin_48647112的博客
01-20 3387
这种写法,括号里的参数并不影响最终的计算结果,常见的用法是传入一个常数值(比如1),因为它会被优化器忽略。因为数据库引擎可以直接计算返回的行数,而无需对具体的列进行操作。然而,在某些特定情况下,可能会使用。是 SQL 用于统计行数的两种常见方式,它们的区别在于统计的对象不同。代表所有的列,因为通配符会展开成所有的列名,并且不会受到空值的影响。会统计查询结果集非空行的数量,相当于计算满足条件的行数。是更常见和更通用的用法,可以直接统计查询结果的总行数。会统计查询结果集的所有行数,包括空行和非空行。
count(*)和count(1)的区别是什么?
weixin_43980049的博客
04-16 2万+
个人总结如下: 一、count(*)和count(1)查询速度 使用count函数,当要统计的数量比较大时,发现count(*)花费的时间比较多,相对来说count(1)花费的时间比较少。 1、如果你的数据表没有主键,那么count(1)比count(*)快 ;如果有主键的话,那主键(联合主键)作为count的条件也比count(*)要快 。 2、如果你的表只有一个字段的话那count(...
COUNT(1)和COUNT(*) 的区别
xintingandzhouyang的专栏
03-21 1万+
两者本质上没有任何区别。 参见MYSQL 参考手册 InnoDBhandlesSELECT COUNT(*)andSELECT COUNT(1)operations in the same way. There is no performance difference. 一、count情况 1、count(1):可以统计表所有数据,不统计所有的列,...
MySQLCOUNT(1)和COUNT(*) 的区别
Fanzongshen的博客
09-19 4093
MySQLCOUNT(1)和COUNT(*) 的区别
MySQL count(*) 为什么那么慢?
06-08
MySQL count(*) 操作是用来统计某个表的行数的,但是当表数据量较大时,执行 count(*) 操作会非常慢,原因如下: 1. 扫描全表:执行 count(*) 操作时,MySQL 需要扫描整个表,这个过程消耗的时间与表的行数成正比,因此当表数据量非常大时,执行时间也会很长。 2. 锁表:在执行 count(*) 操作时,MySQL 会对表进行加锁以防止其他操作对表数据造成影响,这会导致其他操作需要等待解锁才能继续执行,从而影响整个系统的性能。 针对这个问题,我们可以采取以下一些方法来优化 count(*) 操作的性能: 1. 使用 LIMIT:如果只需要知道表的前几行数据,可以使用 LIMIT 子句来限制查询的行数,这样 MySQL 只需要扫描前几行就可以完成 count(*) 操作,从而提高查询性能。 2. 使用索引:如果表有合适的索引,可以通过使用索引来加速 count(*) 操作,从而提高查询性能。 3. 缓存结果:如果 count(*) 操作的结果不需要实时更新,可以将结果缓存起来,从而避免每次查询都需要扫描整个表,从而提高查询性能。 需要根据具体情况选择合适的方法来优化 count(*) 操作的性能
写文章

热门文章

  • 小学六年级的读者,自学计算机,会爬虫,搞崩过学校网站,还有 Girlfriend..... 319912
  • 数据结构,计算机网络,数据库,计算机组成原理,操作系统有哪些好的网课值得推荐? 81619
  • CSAPP《深入理解计算系统》,我的学习心得 56494
  • 一口气搞懂「文件系统」,就靠这 25 张图了 54130
  • 有什么优质的计算机专业书籍?操作系统、计算机网络、计算机组成、数据结构、数据库..... 48080

分类专栏

  • 图解MySQL 20篇
  • 图解计算机网络 44篇
  • 图解Redis 15篇
  • 图解操作系统 29篇
  • 程序人生 12篇
  • 学习路线 7篇
  • Linux性能调优 3篇
  • sequoiadb 2篇
  • Redis 1篇
  • C++基础知识 15篇
  • 数据结构与算法 1篇
  • 快速入门Shell编程 9篇
  • Linux 11篇
  • Shell 11篇
  • FFmpeg 1篇
  • C/C++ 19篇

最新评论

  • 答应我,这次搞懂 I/O 多路复用!

    努力学编程111: 蹲一手大佬的回答

  • 为了拿捏 Redis 数据结构,我画了 40 张图(完整版)

    Lucky Kyte: 噢,原来是一个复合的结构

  • 为了拿捏 Redis 数据结构,我画了 40 张图(完整版)

    Lucky Kyte: zset是跳表和压缩列表吧,在跳表介绍那里好像写错了

  • TCP 四次挥手,可以变成三次挥手吗?

    小木子先生: 调用了shutdown已经失去了发送能力,那为什么还可以发送确认数据的ACK

  • 互斥锁、自旋锁、读写锁、悲观锁、乐观锁的应用场景

    努力学编程111: 以下是多线程的入口函数,while循环是实现的自旋锁,在多线程使用这个函数的时候,并没有上锁,而是先比较更新,否则重置。从这个角度,个人认为自旋锁是乐观锁。 void increment() { int expected = count.load(); while (!count.compare_exchange_weak(expected, expected + 1)) { // 如果交换失败,expected 会更新为 count 的当前值 // 然后循环再次尝试 expected = count.load(); } }

大家在看

  • javaweb基于JSP+Servlet开发简单的图书馆管理系统源码 课程设计 大作业
  • 【异常检测算法‌】基于聚类方法的异常检测算法‌
  • 毕设分享《基于jsp羽毛球馆管理系统的设计与实现》(源码+lw+解析等) 703
  • 顺序表的使用

最新文章

  • 又发现一个国内超好用的 AI 开放平台!
  • 最强Java面试题,共20万字,涵盖多家一线互联网公司面试真题,新鲜出炉!
  • 执行 select ... for update 语句,如果查询条件没有索引字段的话,是加行锁还是加表锁?
2024年2篇
2022年58篇
2021年49篇
2020年37篇
2019年30篇

目录

目录

评论 20
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值

玻璃钢生产厂家武陟玻璃钢雕塑费用福建户外商场美陈价格深圳室内商场美陈销售厂家亳州小品系列玻璃钢雕塑厂家德州玻璃钢浮雕人物雕塑公司邯郸树脂玻璃钢雕塑河北玻璃钢人物雕塑价格哈尔滨玻璃钢景观雕塑宿州玻璃钢雕塑产品宁德玻璃钢仿铜雕塑厂家供应新乡玻璃钢人物室外不锈钢雕塑临汾广场玻璃钢雕塑玻璃钢仿真海豚雕塑成都商场美陈销售公司安顺玻璃钢景观雕塑价格上海秋季商场美陈供应成都生产玻璃钢面包雕塑蒙自市玻璃钢雕塑设计定制制作玻璃钢雕塑厂电话陕西公园玻璃钢雕塑供应商杭州个性化玻璃钢雕塑销售电话德兴玻璃钢仿铜雕塑大理市玻璃钢雕塑设计多少钱山东动物玻璃钢雕塑多少钱浙江玻璃钢抽象雕塑宁夏城市标志玻璃钢雕塑玻璃钢雕塑施工视频辽宁玻璃钢鹿雕塑青州鹏嘉玻璃钢花盆倾销玻璃钢蚂蚁雕塑香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声单亲妈妈陷入热恋 14岁儿子报警汪小菲曝离婚始末遭遇山火的松茸之乡雅江山火三名扑火人员牺牲系谣言何赛飞追着代拍打萧美琴窜访捷克 外交部回应卫健委通报少年有偿捐血浆16次猝死手机成瘾是影响睡眠质量重要因素高校汽车撞人致3死16伤 司机系学生315晚会后胖东来又人满为患了小米汽车超级工厂正式揭幕中国拥有亿元资产的家庭达13.3万户周杰伦一审败诉网易男孩8年未见母亲被告知被遗忘许家印被限制高消费饲养员用铁锨驱打大熊猫被辞退男子被猫抓伤后确诊“猫抓病”特朗普无法缴纳4.54亿美元罚金倪萍分享减重40斤方法联合利华开始重组张家界的山上“长”满了韩国人?张立群任西安交通大学校长杨倩无缘巴黎奥运“重生之我在北大当嫡校长”黑马情侣提车了专访95后高颜值猪保姆考生莫言也上北大硕士复试名单了网友洛杉矶偶遇贾玲专家建议不必谈骨泥色变沉迷短剧的人就像掉进了杀猪盘奥巴马现身唐宁街 黑色着装引猜测七年后宇文玥被薅头发捞上岸事业单位女子向同事水杯投不明物质凯特王妃现身!外出购物视频曝光河南驻马店通报西平中学跳楼事件王树国卸任西安交大校长 师生送别恒大被罚41.75亿到底怎么缴男子被流浪猫绊倒 投喂者赔24万房客欠租失踪 房东直发愁西双版纳热带植物园回应蜉蝣大爆发钱人豪晒法院裁定实锤抄袭外国人感慨凌晨的中国很安全胖东来员工每周单休无小长假白宫:哈马斯三号人物被杀测试车高速逃费 小米:已补缴老人退休金被冒领16年 金额超20万

玻璃钢生产厂家 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化