目录
序言
一谈到mysql优化,肯定有很多人会提到count(*)优化,百度到的面试题和各种博客,一查一大堆。count(*),真的需要优化吗,如果需要优化那该怎么优化,本文将会从mysql5.6.46,、5.7.16、5.7.26三个版本分别讨论。讨论的前提是innodb搜素引擎。
对比
5.7.26
运行环境
联想拯救者Y7000
表结构
数据量
1006950
只有主键索引时
EXPLAIN select count(*) from employees;---->0.581 0.657 0.572 0.689 0.638
EXPLAIN select count(0) from employees;---> 0.600 0.669 0.531 0.739 0.587
EXPLAIN select count(id) from employees;--->0.651 0.670 0.612 0.636 0.553
以上三条执行结果都是下图
EXPLAIN select count(name) from employees;--->0.717 0.681 0.668 0.715 0.714
前三条的执行计划是一样的,速度也都是相差不大,都使用到了主键索引。
而第四条没有使用到索引,虽然扫描行数和前三条一样,由于需要进行NULL值判断,所以速度稍微慢了点
得出结论:从执行计划和执行速度上看要count(*) count(0) count(id)使用任何一个都可以。而count(普通列)速度可能会稍微慢了点但是不太影响,主要它结果不计null值,所以根据需求使用
使用普通索引
create index idx_name on employees(name)
EXPLAIN select count(*) from employees;---->0.549 0.525 0.506 0.550 0.524 0.498
EXPLAIN select count(0) from employees;---> 0.605 0.438
EXPLAIN select count(id) from employees;--->。。。
EXPLAIN select count(name) from employees;--->。。。
同样以上四条条执行结果都是下图
为什么前三条都走了普通索引,即使第三条都指定主键id。这就需要了解innodb的数据和索引保存结构了。简单的来说主键索引树保存了主键和其它所有数据,而普通索引树保存了该索引列和对应的主键,首先从树大小来看,普通索引树更小搜索更快,所以mysql底层优先走了普通索引。
第四条虽然执行计划也是和上三条一样,但是它指定了列,并且它是可以为null的,所以它在计算的时候不把null值计算在内,需要进行判断是否null值处理。而上三条只需计数不需取值判断是否为null
5.7.16
运行环境
联想天逸510Pro
表结构
同上
数据量
548626
只有主键索引时
EXPLAIN select count(*) from employees;---->0.639 0.652 0.866 0.729 0.605 0.705
EXPLAIN select count(0) from employees;--->0.652 0.620 0.632 0.633 0.645 0.711
EXPLAIN select count(id) from employees;--->0.621 0.648 0.612 0.628 0.681 0.643
以上三条执行结果都是下图
EXPLAIN select count(name) from employees;--->0.678 0.639 0.611 0.641 0.631 0.635
这个版本的mysql前三条语句竟然都没有使用到索引,而使用了Select tables optimized away,优化器认为可以在优化阶段读取要读取的行(例如,通过读取索引行)时,不需要在查询执行期间读取任何表。意思是在优化阶段就可以获取到所需要的值,已经是很优的sql了。但和第四条语句速度相差可以忽略不计(这个结果仅供参考)
使用普通索引
create index idx_name on employees(name)
前三条同上
EXPLAIN select count(name) from employees;--->0.239 0.171 0.136 0.128 0.145
我靠,可怕吗,第三个走了idx_name索引竟然快了这么多。
可说明 5.7.16到5.7.26已经多count又做了一次优化
5.6.47
执行计划和5.7.26一模一样
总结
(1)count(*) count(0) count(主键)底层执行计划是一样的,而且mysql优化器会将其优化成count(0) (trace后可见),所以说count(*)没有必要进行优化。
(2)如果需要全表count(*)而且表比较大的话,最好表中有辅助索引(非主键索引)
(3)如果需要范围count(*),请给范围查找的字段加合适的索引,否则会进行全表扫描 where语句优化和其它类型的优化都需要注意哦
(4)count(*) count(0) count(主键)会计入NULL,count(可为null的列)不会计入NULL。所以除非需要只计算固定列为不为NULL的数据时,才使用count(列)
(5)请放心大胆的使用count(*)吧,注意(2)(3)(4)条就可以喽