【MySQL】性能优化之 count(*) VS count(col)

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 优化mysql数据库时,经常有开发询问 count(1)和count(primary_key) VS count(*)的性能有何差异?看似简单的问题,估计会有很多人对此存在认知误区:1. 认为count(1) 和 count(primary_key) 比 count(*) 的性能好。
优化mysql数据库时,经常有开发询问 count(1)和count(primary_key) VS count(*)的性能有何差异?看似简单的问题,估计会有很多人对此存在认知误区:
1. 认为count(1) 和 count(primary_key) 比 count(*) 的性能好。
2. count(column) 和 count(*) 效果是一样的。
本文对上述两点做如下测试,
测试环境:
root@yang 07:17:04>CREATE TABLE `mytab` (
    ->   `id` int(10) unsigned NOT NULL,
    ->   `v1` int(11) default NULL,
    ->   `v2` int(10) unsigned NOT NULL,
    ->   KEY `idx_id` (`id`)
    -> ) ENGINE=MyISAM DEFAULT CHARSET=latin1;
Query OK, 0 rows affected (0.00 sec)
1 select语句 不含有where 条件
root@yang 07:41:11>select count(*) from mytab;
+----------+
| count(*) |
+----------+
|  2000000 |
+----------+
1 row in set (0.00 sec)
root@yang 07:41:22>select count(id) from mytab; 
+-----------+
| count(id) |
+-----------+
|   2000000 |
+-----------+
1 row in set (0.00 sec)
root@yang 07:41:37>select count(v1) from mytab;  
+-----------+
| count(v1) |
+-----------+
|   2000000 |
+-----------+
1 row in set (0.12 sec)
root@yang 07:41:41>select count(v2) from mytab; 
+-----------+
| count(v2) |
+-----------+
|   2000000 |
+-----------+
1 row in set (0.00 sec)
以上使用了myisam表做了测试,四种查询方式的结果有所不同,注意到count(V1) 的时间是0.12s 因为myisam 表的特性其已经保存了表的总行数, count(*)相对非常快。
coun(v2) 比count(v1) 快是因为v1 字段可以为空,mysql 在执行count(col) 是表示结果集中有多少个col字段不为空的记录,mysql 存储引擎会去检查表中说有行记录中col字段是否为空,并计算出非空的个数。

2 使用带有where 条件的查询:
root@yang 01:13:13>select count(*) from t1 where id
+----------+
| count(*) |
+----------+
|   200000 |
+----------+
1 row in set (0.06 sec)
root@yang 01:15:22>explain select count(*) from t1 where id
+----+-------------+-------+-------+---------------+--------+---------+------+--------+--------------------------+
| id | select_type | table | type  | possible_keys | key    | key_len | ref  | rows   | Extra                    |
+----+-------------+-------+-------+---------------+--------+---------+------+--------+--------------------------+
|  1 | SIMPLE      | t1    | range | idx_id        | idx_id | 4       | NULL | 205923 | Using where; Using index |
+----+-------------+-------+-------+---------------+--------+---------+------+--------+--------------------------+
1 row in set (0.00 sec)
root@yang 01:15:30> select count(v1) from t1 where id
+-----------+
| count(v1) |
+-----------+
|    200000 |
+-----------+
1 row in set (0.17 sec)
root@yang 01:15:37>explain  select count(v1) from t1 where id
+----+-------------+-------+-------+---------------+--------+---------+------+--------+-------------+
| id | select_type | table | type  | possible_keys | key    | key_len | ref  | rows   | Extra       |
+----+-------------+-------+-------+---------------+--------+---------+------+--------+-------------+
|  1 | SIMPLE      | t1    | range | idx_id        | idx_id | 4       | NULL | 205923 | Using where |
+----+-------------+-------+-------+---------------+--------+---------+------+--------+-------------+
1 row in set (0.00 sec)
root@yang 01:15:42> select count(v2) from t1 where id
+-----------+
| count(v2) |
+-----------+
|    200000 |
+-----------+
1 row in set (0.16 sec)
root@yang 01:15:49>explain select count(v2) from t1 where id
+----+-------------+-------+-------+---------------+--------+---------+------+--------+-------------+
| id | select_type | table | type  | possible_keys | key    | key_len | ref  | rows   | Extra       |
+----+-------------+-------+-------+---------------+--------+---------+------+--------+-------------+
|  1 | SIMPLE      | t1    | range | idx_id        | idx_id | 4       | NULL | 205923 | Using where |
+----+-------------+-------+-------+---------------+--------+---------+------+--------+-------------+
1 row in set (0.00 sec)
count(*) 可以使用覆盖索引 ,而count(col)不行。v2 是非空列,处理起来应该和count(*)类似才对,这里显示却和v1 字段的处理一致。如果mysql 优化器处理的足够好,检查到字段为非空时,即可和count(*) 做同样的处理,这样速度会更快一些。下面修改索引结构使用复合索引。
root@yang 01:17:07>alter table t1 drop key idx_id,add key idx_id_v1(id,v1);
Query OK, 2000000 rows affected (1.49 sec)
Records: 2000000  Duplicates: 0  Warnings: 0
root@yang 01:17:36> select count(v1) from t1 where id
+-----------+
| count(v1) |
+-----------+
|    200000 |
+-----------+
1 row in set (0.07 sec)
root@yang 01:17:49>explain  select count(v1) from t1 where id
+----+-------------+-------+-------+---------------+-----------+---------+------+--------+--------------------------+
| id | select_type | table | type  | possible_keys | key       | key_len | ref  | rows   | Extra                    |
+----+-------------+-------+-------+---------------+-----------+---------+------+--------+--------------------------+
|  1 | SIMPLE      | t1    | range | idx_id_v1     | idx_id_v1 | 4       | NULL | 196079 | Using where; Using index |
+----+-------------+-------+-------+---------------+-----------+---------+------+--------+--------------------------+
1 row in set (0.00 sec)
对于字段v1 的查询性能相对上例中提升两倍多,当然如果是生产环境可能提升更高。最终面向开发是,最好先有开发修改应用程序中的sql 避免使其选择count(col)。
对于第二个误区:认为 count(column) 和 count(*) 是一样的,其实是有差别的。请看下面的例子:
root@yang 10:01:38>create table t3 (id int ,v1 int ) engine =innodb;
Query OK, 0 rows affected (0.01 sec)
root@yang 10:03:54>insert t3 values (null,null),(1,null),(null,1),(1,null),(null,1),(1,null),(null,null);
Query OK, 7 rows affected (0.00 sec)
Records: 7  Duplicates: 0  Warnings: 0
root@yang 10:03:57>select count(id),count(id),count(v1) from t3;
+-----------+-----------+-----------+
| count(id) | count(id) | count(v1) |
+-----------+-----------+-----------+
|         3 |         3 |         2 |
+-----------+-----------+-----------+
1 row in set (0.00 sec)
count(col) 是表示结果集中有多少个column字段不为空的记录。
count(*) 是表示整个结果集有多少条记录。
3 增加对innodb 存储引擎的测试
root@yang 01:29:53>alter table t1 engine=innodb;
Query OK, 2000000 rows affected (11.25 sec)
Records: 2000000  Duplicates: 0  Warnings: 0
root@yang 01:30:26>select count(*) from t1;
+----------+
| count(*) |
+----------+
|  2000000 |
+----------+
1 row in set (0.69 sec)
对于 innodb表比myisam 表查询总行数 性能慢是因为innodb 存储引擎并没有保存行的总数,innodb 表支持mvcc ,不同的事务可能看到不同的行记录数。因此每次count(*) 和count(col)(没有使用索引的情况下) 都要对表进行索引扫描,可能大家对最终获取结果的时间有疑问,为什么myisam 表是0.17s 左右,而innodb 是0.77s ,因为innodb 表在磁盘存储的大小比myisam大,扫描的物理page更多。
root@yang 01:30:32>select count(v1) from t1; 
+-----------+
| count(v1) |
+-----------+
|   2000000 |
+-----------+
1 row in set (0.77 sec)
root@yang 01:30:40>select count(v2) from t1; 
+-----------+
| count(v2) |
+-----------+
|   2000000 |
+-----------+
1 row in set (0.73 sec)
在使用where条件的情况下:等值查询和使用到索引情况下 ,myisam 表和innodb的速度是几乎无差别的,具体的性能表现和where 条件有关。 
root@yang 10:14:03>select count(v2) from t1 where id =20000;  
+-----------+
| count(v2) |
+-----------+
|         1 |
+-----------+
1 row in set (0.00 sec)

推荐阅读:
相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
26天前
|
存储 缓存 关系型数据库
MySQL的count()方法慢
MySQL的 `COUNT()`方法在处理大数据量时可能会变慢,主要原因包括数据量大、缺乏合适的索引、InnoDB引擎的设计以及复杂的查询条件。通过创建合适的索引、使用覆盖索引、缓存机制、分区表和预计算等优化方案,可以显著提高 `COUNT()`方法的执行效率,确保数据库查询性能的提升。
631 12
|
1月前
|
缓存 NoSQL 关系型数据库
MySQL战记:Count( *)实现之谜与计数策略的选择
本文深入探讨了MySQL中`count(*)`的不同实现方式,特别是MyISAM和InnoDB引擎的区别,以及各种计数方法的性能比较。同时,文章分析了使用缓存系统(如Redis)与数据库保存计数的优劣,并强调了在高并发场景下保持数据一致性的挑战。
MySQL战记:Count( *)实现之谜与计数策略的选择
|
2月前
|
SQL 关系型数据库 MySQL
MySQL性能探究:count(*)与count(1)的性能对决
在MySQL数据库的性能优化中,对查询语句的细微差别有着深入的理解是非常重要的。`count(*)`和`count(1)`是两种常用的聚合函数,用于计算行数。在面试中,面试官经常会问到这两种函数的性能差异。本文将探讨`count(*)`与`count(1)`的性能对比,并整理十道经典的MySQL面试题,帮助你在面试中游刃有余。
110 3
|
2月前
|
存储 缓存 负载均衡
mysql的性能优化
在数据库设计中,应选择合适的存储引擎(如MyISAM或InnoDB)、字段类型(如char、varchar、tinyint),并遵循范式(1NF、2NF、3NF)。功能上,可以通过索引优化、缓存和分库分表来提升性能。架构上,采用主从复制、读写分离和负载均衡可进一步提高系统稳定性和扩展性。
48 9
|
2月前
|
存储 关系型数据库 MySQL
MySQL vs. PostgreSQL:选择适合你的开源数据库
在众多开源数据库中,MySQL和PostgreSQL无疑是最受欢迎的两个。它们都有着强大的功能、广泛的社区支持和丰富的生态系统。然而,它们在设计理念、性能特点、功能特性等方面存在着显著的差异。本文将从这三个方面对MySQL和PostgreSQL进行比较,以帮助您选择更适合您需求的开源数据库。
235 4
|
2月前
|
关系型数据库 MySQL 索引
MySQL的group by与count(), *字段使用问题
正确使用 `GROUP BY`和 `COUNT()`函数是进行数据聚合查询的基础。通过理解它们的用法和常见问题,可以有效避免查询错误和性能问题。无论是在单列分组、多列分组还是结合其他聚合函数的场景中,掌握这些技巧和注意事项都能大大提升数据查询和分析的效率。
269 0
|
3月前
|
存储 关系型数据库 MySQL
MySQL性能优化实践指南
【10月更文挑战第16天】MySQL性能优化实践指南
250 0
|
3月前
|
存储 关系型数据库 MySQL
MySQL性能优化指南
【10月更文挑战第16天】MySQL性能优化指南
317 0
|
3月前
|
存储 关系型数据库 MySQL
深度剖析:MySQL聚合函数 count(expr) 如何工作?如何选择?
本文详细探讨了MySQL中count(expr)函数的不同形式及其执行效率,包括count(*)、count(1)、count(主键)、count(非主键)等。通过对InnoDB和MyISAM引擎的对比分析,解释了它们在不同场景下的实现原理及性能差异。文章还通过实例演示了事务隔离级别对统计结果的影响,并提供了源码分析和总结建议。适合希望深入了解MySQL统计函数的开发者阅读。
79 0
|
4月前
|
SQL 存储 关系型数据库
深入 MySQL 的执行计划与性能优化
深入 MySQL 的执行计划与性能优化
50 0