吾被骗益久了!Count(*) 性能最差?
作者:admin    发布时间: 2022-01-07 22:05

 

行家益,吾是幼林。

当吾们对一张数据外中的记录进走统计的时候,民俗都会行使 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。也就是说,在新添和删除操作时,吾们必要额外维护这个计数外。

【编辑选举】

鸿蒙官方战略配相符共建——HarmonyOS技术社区2022年大数据工程师薪资和做事趋势2022年,如何选择特出数据分析工具?轻盈搞跨数据治理,吾们就靠这7招!终于有人把数据发掘讲清新了Sentry 企业级数据坦然解决方案 - Relay 运走模式

Powered by 2021求个直接能用的网站-十大黄软排行榜-求大神给个靠谱的 @2018 RSS地图 HTML地图

Copyright 365建站 © 2013-2021 365建站器 版权所有