以下讨论是基于InnoDB存储引擎,这也是mysql默认的存储引擎。
count 函数的语义
要想弄清楚count(*)、count(1)、count(主键id)、count(字段)的区别,需要弄清楚count函数的语言,该函数拿到传进来的参数后,会一行一行地遍历结果返回集,如果某行参数不为Null,就累计计数加1,如果某行参数为Null,就跳过。count(字段)就会判断该行的某个字段是否Null来决定累计计数是否加1
分析性能差异时遵守的原则
在分析count(*)、count(1)、count(主键id)、count(字段)的性能区别时,遵守以下几个原则:
- server层要什么就给什么
- InnoDB只给必要的值
- mysql优化器只对count(*)语句做了优化
不同count参数执行过程
count(主键id)
mysql server层执行器调用 count(主键id),InnoDB存储引擎发现server层需要的是主键id。于是,InnoDB会遍历整张表,把每一行的主键id都取出来,返回给server层。server层执行器拿到主键id后,判断其是否为空,不为空,累计计数加1。注意:主键是不允许为空的,所以累计计数一定会加1,这是显而易见的,但是server层还是要判断一下
count(1)
mysql server层执行器调用 count(1),InnoDB存储引擎发现server层需要的是1。于是,InnoDB会遍历整张表,把每一行取出来,但是不取任何值,而是把该行返回给server层。server层对于返回的每一行,放一个数字"1"进去,然后判断1是不是为空,不为空累计计数就加1.注意:server层放一个数字1,1一定是不为空,这是显而易见的,但是server层还是要判断一下1是否为空
count(字段)
mysql server层执行器调用 count(字段),InnoDB存储引擎发现server层需要的是某个字段。于是,InnoDB会遍历整张表,把每一行的目标字段都取出来,返回给server层。server层执行器拿到目标字段后,判断其是否为空,不为空,累计计数加1。注意:如果目标字段在定义的时候不允许为空的话,在用该count(字段)时累计计数一定会加1,这是显而易见的,但是server层还是要判断一下
count(*)
mysql对count()语句做了专门的优化,InnoDB看到是count(*),并不会取值,而是按行累加。应为count()就是按行累计,这是显而易见的。
为什么count(1)、count(主键id),count(非空字段)按行累加是显而易见的,但mysql却没有优化它们?
因为这种专门优化的情况太多了,而且mysql已经优化过count(*)了,你直接使用就行了。
结论
按照效率排序:count(字段)<count(主键id)<count(1)<count(*)。
所以尽量使用count(*)
为什么count(主键id)<count(1)?
因为count(主键id)涉及到解析行数据,拷贝字段。而count(1)直接返回行,所以count(1)效率更快