相信大家都有听过,MyISAM 计算count值是非常快的,甚至有很多文章说MyISAM count值的计算快于InnoDb。因为MyISAM会存储count值。
的确是的,不过要注意的是,MyISAM存储的是行数而不是列数【且是无条件下】,这个有什么区别呢。
看语句:
select count(*) from xxx;
select count(columnName) from xxx;
相对于这2个sql,对于MyISAM
和InnoDb
都是第一个相对较快,MyISAM 快的原因是存储了总行数,
2个引擎索引列的总数时,要考虑一个重要的因素便是把该列为null
的排除掉,换言之,总行数100,假如count某列时,设置该列可为空,正好有1行的该列没有值,为null,那么该列的count值就是100- count(colunName==null))
。这就是创建表结构时,把列基本都设置成not null
的原因之一了。
所以不管是MyISAM
和InnoDb
,在不要求极力求算某列count值,或者可以保证该列为not null
时都可以直接计算count(*)
,MyISAM存储了总行数,而InnoDb不需要再次判断该列是否为空的的问题了。 so 小 count 大学问。
那么问题来了,假如说count的列带有索引,且该列not null
,对于InnoDb而言是不是count列就更快了呢。
实践得出真实,来实验下吧。
explain SELECT count(*) from users;
explain SELECT count(name) from users;
这2个执行计划,其实执行计划都是一模一样的,(其实从简单的执行计划上来看是看不出什么问题的。)
+----+-------------+-------+------------+-------+---------------+----------+---------+------+---------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+----------+---------+------+---------+----------+-------------+
| 1 | SIMPLE | users | NULL | index | NULL | idx_name | 767 | NULL | 2892750 | 100.00 | Using index |
+----+-------------+-------+------------+-------+---------------+----------+---------+------+---------+----------+-------------+
1 row in set, 1 warning (0.03 sec)
这是为什么呢?
简单从执行计划上看是行不通的,因为InnoDB
在查询count值时,一般会经历这几个步骤【可以看下查询语句的执行过程:一条查询SQL如何执行都不知道,你和咸鱼有什么区别】
下面将只讨论InnoDB
引擎。
首先计算count(*)
值时,选择最小索引数进行计算,一般都是最小二级索引【索引key最小的那棵树】,二级索引保存的数据是主键,而主键一般不为空,所以count(*) 和 count(1) ,count(id)并没有所谓的快慢区分,都表示返回满足条件的结果集的总行数。
不过有必要在说下count的底层说明
1.count(1) :InnoDB 引擎遍历整张表,但不取值。server 层对于返回的每一行,放一个数字“1”进去,判断是不可能为空的,按行累加。
InnoDB handles SELECT COUNT( * ) and SELECT COUNT(1) operations in the same way. There is no performance difference.
--来源MySQL文档:https://dev.mysql.com/doc/refman/5.7/en/group-by-functions.html#function_count
----------------------华丽分割线-----------------
大意便是innodb以相同的方式处理count(*)和count(1),没有性能差异。
2.count(*):返回获取的行数的计数,无论它们是否包含 NULL值。
在MySQL 5.7.18之前,InnoDB通过扫描聚集索引来处理 SELECT COUNT( * )语句。从MySQL 5.7.18开始, 除非有索引或优化器提示指示优化器使用其他索引,否则InnoDB通过遍历最小的可用二级索引来处理SELECT COUNT(*)语句。如果不存在二级索引,则将扫描聚集索引。
----mysql 文档:https://dev.mysql.com/doc/refman/5.7/en/group-by-functions.html#function_count
3.count(id):,InnoDB 引擎会遍历整张表,把每一行的 id 值都取出来,返回给 server 层。server 层拿到 id 后,判断是不可能为空的,就按行累加。不过count(id)相比于前2个还是稍微慢点的。但是程度是肉眼无法感知的。
4.count(columnName): 这个是最慢的,来看看计算这个值时,InnoDB都做了些什么吧。
第一种情况:在该字段定义为not null
情况下,一行行地从记录里面读出这个字段,判断不能为 null,按行累加;
第二种情况:允许为null
,那么在执行时,判断到可能为null
,那么需要做的就是把该值取出来,判断是否为null,不为null,进行累加。
看到了吧,如果不定义字段为not null
是会进行赋值判断的,是个非常糟糕的情况。
所以在使用InnoDB
时,计算count值时优先顺序为:
5.7 版本:count(*) = count(1) > count(id) > count(columnName)
5.7以下版本:count(*) ≈ count(1) > count(id) > count(columnName)
网友评论