高性能MySQL——Count(1) OR Count(*)?
如果问一个程序员MySQL中SELECT COUNT(1)和SELECT COUNT(*)有什么区别,会有很多人给出这样的答案“SELECT COUNT(*)”最终会转化成“SELECT COUNT(1),而SELECT COUNT(1)省略了转换的这一步,所以SELECT COUNT(1)效率更高“,甚至有一些面试官也会给出类似的答案。最近在看一些历史遗留代码,绝大多数统计数量的SQL都在用SELECT COUNT(1),觉得有必要搞清楚这个问题。
首先,以我们最常见的两种数据库表引擎MyISAM和Innodb来讲。
MyISAM
MyISAM在统计表的总行数的时候会很快,但是有个大前提,不能加有任何WHERE条件。这是因为:MyISAM对于表的行数做了优化,具体做法是有一个变量存储了表的行数,如果查询条件没有WHERE条件则是查询表中一共有多少条数据,MyISAM可以做到迅速返回,所以也解释了如果加WHERE条件,则该优化就不起作用了。细心的同学会发现,innodb的表也有这么一个存储了表行数的变量,但是很遗憾这个值是一个估计值,没有什么实际意义。
Innodb
在该引擎下,COUNT(1)和COUNT(*)哪个快呢?结论是:这俩在高版本的MySQL(5.5及以后,5.1的没有考证)是没有什么区别的,也就没有COUN(1)会比COUNT(*)更快这一说了。
WHY?这就要从COUNT()函数的具体含义说起了。”COUNT()有两个非常不同的作用:它可以统计某个列值的数量,也可以统计行数。在统计列值时要求列值是非空的(不统计NULL)。如果在COUNT()的括号中定了列或者列表达式,则统计的就是这个表达式有值的结果数。......COUNT()的另一个作用是统计结果集的行数。当MySQL确认括号内的表达式值不可能为空时,实际上就是在统计行数。最简单的就是当我们使用COUNT(*)的时候,这种情况下通配符*并不像我们猜想的那样扩展成所有的列,实际上,他会忽略所有列而直接统计所有的行数“——《高性能MySQL》。
通常,我们将第一个字段(一般是ID)作为主键,那么这个时候COUNT(1)实际统计的就是行数,因为主键肯定是非NULL的。问题是Innodb是通过主键索引来统计行数的吗?如果该表之后一个主键索引,没有任何二级索引的情况下,那么COUNT(*)和COUNT(1)都是通过通过主键索引来统计行数的。如果该表有二级索引,则COUNT(1)和COUNT(*)都会通过占用空间最小的字段的二级索引进行统计,也就是说虽然COUNT(1)指定了第一列但是innodb不会真的去统计主键索引(一般为第一个字段的索引)。
实验
第一步
新建一张基于Innodb的表,只有一个ID主键,并插入5w的测试数据,DDL如下:
CREATE TABLE `tb_news` (
`id` bigint(21) NOT NULL AUTO_INCREMENT,
`title` varchar(50) NOT NULL,
`content` mediumtext NOT NULL,
`count_ass` char(1) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=50001 DEFAULT CHARSET=utf8
这个时候执行COUNT(1)和COUNT(*)可以看到解释器的结果如下(两者一致,所以就只截了一张图),可以看到,两者都用了主键索引进行行数的统计:
第二步
新建一个二级索引title,之后在分别看一下COUNT(1)和COUNT(*)的解释器结果(两者依然完全一致):
第三步
解释
结论
拓展
推荐阅读
-
count(*)和count(1)的区别和性能分析
-
Mysql--count(*)等聚合函数的使用方法
-
mysql count方法的高级用法示例:
-
MySQL知识点补充(不同count()的用法、union执行流程、group by语句)
-
MySQL:count(*),count(1),count(col)效率比较
-
高性能MySQL——Count(1) OR Count(*)?
-
MySQL基础优化(7):高效count函数
-
count(*)和count(1)的区别
-
1. 创建视图vw_count:查询顾客“马云”一共购买了多少部小米Note2,字段分别为客户姓名(cname),商品名字(pname),物品总数量(osum)
-
关于 MySQL 百万数据量的 count(*) 查询如何优化?