线上易出 bug!COUNT(*)、COUNT(1)、COUNT(某一列)区别别再拎不清了
讨论一个MySQL查询相关的经典问题这个问题也导致出现过不少线上bug也是大家日常工作经常会用到的。不少同学可能使用了多年的COUNT(*)也不是很清楚它与COUNT(1)、COUNT(某一列)的区别让我们今天就一探究竟吧。面试官COUNT(*)、COUNT(1)、COUNT(某一列)的区别是什么哪个性能更好我们还是先用一张图概述所有要点基本概念1、COUNT(*)这个表达式会计算表中的所有行包括那些值为NULL的行。它不考虑列的选择直接统计整个表的行数。MySQL官网[1]中有说明InnoDB通过遍历最小的可用二级索引来处理SELECT COUNT(*)语句除非索引或优化器提示优化器使用不同的索引。如果二级索引不存在InnoDB通过扫描聚簇索引来处理SELECT COUNT(*)语句。为什么要采用遍历最小的可用二级索引而不是直接选聚簇索引因为二级索引不存储具体数据相比聚簇索引占用空间小遍历的I/O效率更高。2、COUNT(1)这是一个优化过的计数方式实际上并不计算任何列的值而是把“1”作为一个常量来对待统计的是满足条件的行数。这种方式避免了访问表的具体列理论上可以提高效率尤其是在列值可为空的情况下。MySQL官网[1]中有说明InnoDB处理SELECT COUNT(*)和SELECT COUNT(1)操作的方式相同。3、COUNT(某一列)这个表达式统计的是指定列中非NULL值的数量。如果该列有NULL值那么这些行不会被计入总数中。区别1、数据处理范围COUNT(*)包括了所有行无论列值是否为NULLCOUNT(1)同样统计所有行因为它计数的是恒定的1不关心行数据的实际内容而COUNT(某一列)只统计该列非NULL值的行数。2、性能影响因素由于COUNT(*)和COUNT(1)不需要访问实际的数据行特别是当索引可以覆盖查询需求时它们的执行效率可能高于COUNT(某一列)特别是当该列含有大量NULL值时。3、应用场景如果你需要统计表的所有行数包括那些具有NULL值的行应使用COUNT(*)或COUNT(1)。若你需要统计特定列的非NULL值数量则应使用COUNT(某一列)。性能比较1、在MySQL中COUNT(*)和COUNT(1)的性能差异微乎其微甚至可以认为是等价的。在MySQL官网中明确指出InnoDB引擎中两语句性能一致[1]。原文如下InnoDB handles SELECT COUNT(*) and SELECT COUNT(1) operations in the same way. There is no performance difference.2、相比之下COUNT(某一列)的性能取决于该列的数据分布特别是NULL值的比例。如果该列包含大量NULL值且查询优化器无法有效利用索引那么它的执行效率可能会低于前两者。其中COUNT(主键)相比COUNT(普通列)的性能要好。3、实际性能还受到数据库索引、表结构、数据量、以及数据库管理系统具体实现等因素的影响。因此在特定场景下需进行实际测试。结语总的来说COUNT(*)和COUNT(1)在统计全表行数时非常相似且在多数情况下性能相当推荐在不需要考虑特定列的NULL值时使用。而COUNT(某一列)则适用于需要精确统计某列非NULL值数量的场景。在考虑性能优化时了解并利用数据库的特定优化特性和执行计划分析工具可以帮助做出更合适的选择。