mysql field count_MySQL count(*),count(1),count(field)区别、性能差异及优化建议

count函数是用来获取表中满足一定条件的记录数,常见用法有三种,count(*),count(1),count(field),这三种有什么区别?在性能上有何差异?本文将通过测试案例详细介绍和分析。

三者有何区别:

count(field)不包含字段值为NULL的记录。

count(*)包含NULL记录。

select(*)与select(1) 在InnoDB中性能没有任何区别,处理方式相同。官方文档描述如下:

InnoDB handles SELECT COUNT(*) and SELECT COUNT(1) operations in the same way. There is no performance difference.

1. 性能对比

通过案例来测试一下count(*),count(1),count(field)的性能差异,MySQL版本为5.7.19,测试表是一张sysbench生成的表,表名sbtest1,总记录数2411645,如下:

CREATE TABLE sbtest1 (

id int(11) NOT NULL AUTO_INCREMENT,

k int(11) DEFAULT NULL,

c char(120) NOT NULL DEFAULT '',

pad char(60) NOT NULL DEFAULT '',

PRIMARY KEY (id),

KEY k_1 (k)

) ENGINE=InnoDB;

测试SQL语句:

select count(*) from sbtest1;

select count(1) from sbtest1;

select count(id) from sbtest1;

select count(k) from sbtest1;

select count(c) from sbtest1;

select count(pad) from sbtest1;

针对count(*)、count(1)和count(id),加了强制走主键的测试,如下:

select count(*) from sbtest1 force index(primary);

select count(1) from sbtest1 force index(primary);

select count(id) from sbtest1 force index(primary);

另外对不同的测试SQL,收集了profile,发现主要耗时都在Sending data这个阶段,记录Sending data值。

汇总测试结果:

类型

耗时(s)

索引

Sending data耗时(s)

count(*)

0.47

k_1

0.463624

count(1)

0.46

k_1

0.463242

count(id)

0.52

k_1

0.521618

count(*)强制走主键

0.54

primay key

0.538737

count(1)强制走主键

0.55

primary key

0.545007

count(id)强制走主键

0.60

primary key

0.598975

count(k)

0.53

k_1

0.529366

count(c)

0.81

NULL

0.813918

count(pad)

0.76

NULL

0.762040

结果分析:

从以上测试结果来看,count(*)和count(1)性能基本一样,默认走二级索引(k_1),性能最好,这也验证了count(*)和count(1)在InnoDB内部处理方式一样。

count(id) 虽然也走二级索引(k_1),但是性能明显低于count(*)和count(1),可能MySQL内部在处理count(*)和count(1)时做了额外的优化。

强制走主键索引时,性能反而没有走更小的二级索引好,InnoDB存储引擎是索引组织表,行数据在主键索引的叶子节点上,走主键索引扫描时,处理的数据量比二级索引更多,所以性能不及二级索引。

count(c)和count(pad)没有走索引,性能最差,但是明显count(pad)比count(c)好,因为pad字段类型为char(60),小于字段c的char(120),尽管两者性能垫底,但是字段小的性能相对更好些。

2. count(*)延伸

在5.7.18版本之前,InnoDB处理select count(*) 是通过扫描聚簇索引,来获取总记录数。

从5.7.18版本开始,InnoDB扫描一个最小的可用的二级索引来获取总记录数,或者由SQL hint来告诉优化器使用哪个索引。如果二级索引不存在,InnoDB将会扫描聚簇索引。

执行select count(*)在大部分场景下性能都不会太好,尤其是表记录数特别大的情况下,索引数据不在buffer pool里面,需要频繁的读磁盘,性能将更差。

3. count(*)优化思路

一种优化方法,是使用一个统计表来存储表的记录总数,在执行DML操作时,同时更新该统计表。这种方法适用于更新较少,读较多的场景,而对于高并发写操作,性能有很大影响,因为需要并发更新热点记录。

如果业务对count数量的精度没有太大要求,可使用show table status中的行数作为近似值。