其他
我被骗好久了!count(*) 性能最差?
The following article is from 小林coding Author 小林coding
来源丨小林coding(ID:CodingLin)
作者丨小林coding
当我们对一张数据表中的记录进行统计的时候,习惯都会使用 count 函数来统计,但是 count 函数传入的参数有很多种,比如 count(1)、count(*
)、count(字段) 等。到底哪种效率是最好的呢?是不是 count(*
) 效率最差?我曾经以为 count(*
) 是效率最差的,因为认知上 selete * from t
会读取所有表中的字段,所以凡事带有 *
字符的就觉得会读取表中所有的字段,当时网上有很多博客也这么说。但是,当我深入 count 函数的原理后,被啪啪啪的打脸了!不多说, 发车!哪种 count 性能最好?
我先直接说结论:哪种 count 性能最好?
count() 是一个聚合函数,函数的参数不仅可以是字段名,也可以是其他任意表达式,该函数作用是统计符合查询条件的记录中,函数指定的参数不为 NULL 的记录有多少个。假设 count() 函数的参数是字段名,如下:count() 是什么?
在通过 count 函数统计有多少个记录时,MySQL 的 server 层会维护一个名叫 count 的变量。server 层会循环向 InnoDB 读取一条记录,如果 count 函数指定的参数不为 NULL,那么就会将变量 count 加 1,直到符合查询的全部记录被读完,就退出循环。最后将 count 变量的值发送给客户端。InnoDB 是通过 B+ 树来保持记录的,根据索引的类型又分为聚簇索引和二级索引,它们区别在于,聚簇索引的叶子节点存放的是实际数据,而二级索引的叶子节点存放的是主键值,而不是实际数据。用下面这条语句作为例子:count(主键字段) 执行过程是怎样的?
select count(id) from t_order;
用下面这条语句作为例子:count(1) 执行过程是怎样的?
看到count(*) 执行过程是怎样的?
*
这个字符的时候,是不是大家觉得是读取记录中的所有字段值?对于 selete *
这条语句来说是这个意思,但是在 count(*) 中并不是这个意思。count(*
) 其实等于 count(0
),也就是说,当你使用 count(*
) 时,MySQL 会将 *
参数转化为参数 0 来处理。*
) 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(1)、 count(*)、 count(主键字段) 执行效率是最差的。用下面这条语句作为例子:count(字段) 执行过程是怎样的?
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 条记录,现在有两个会话并行以下语句:如何优化 count(*)?
如果对一张大表经常用 count(*) 来做统计,其实是很不好的。比如下面我这个案例,表 t_order 共有 1200+ 万条记录,我也创建了二级索引,但是执行一次select count(*) from t_order
要花费差不多 5 秒!2、西安一码通分析!
点分享
点点赞
点在看