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

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS MySQL,高可用系列 2核4GB
简介: 优化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)

推荐阅读:
相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
10天前
|
存储 缓存 负载均衡
mysql的性能优化
在数据库设计中,应选择合适的存储引擎(如MyISAM或InnoDB)、字段类型(如char、varchar、tinyint),并遵循范式(1NF、2NF、3NF)。功能上,可以通过索引优化、缓存和分库分表来提升性能。架构上,采用主从复制、读写分离和负载均衡可进一步提高系统稳定性和扩展性。
31 9
|
23天前
|
存储 关系型数据库 MySQL
MySQL vs. PostgreSQL:选择适合你的开源数据库
在众多开源数据库中,MySQL和PostgreSQL无疑是最受欢迎的两个。它们都有着强大的功能、广泛的社区支持和丰富的生态系统。然而,它们在设计理念、性能特点、功能特性等方面存在着显著的差异。本文将从这三个方面对MySQL和PostgreSQL进行比较,以帮助您选择更适合您需求的开源数据库。
90 4
|
2月前
|
存储 SQL 关系型数据库
【MySQL调优】如何进行MySQL调优?从参数、数据建模、索引、SQL语句等方向,三万字详细解读MySQL的性能优化方案(2024版)
MySQL调优主要分为三个步骤:监控报警、排查慢SQL、MySQL调优。 排查慢SQL:开启慢查询日志 、找出最慢的几条SQL、分析查询计划 。 MySQL调优: 基础优化:缓存优化、硬件优化、参数优化、定期清理垃圾、使用合适的存储引擎、读写分离、分库分表; 表设计优化:数据类型优化、冷热数据分表等。 索引优化:考虑索引失效的11个场景、遵循索引设计原则、连接查询优化、排序优化、深分页查询优化、覆盖索引、索引下推、用普通索引等。 SQL优化。
543 15
【MySQL调优】如何进行MySQL调优?从参数、数据建模、索引、SQL语句等方向,三万字详细解读MySQL的性能优化方案(2024版)
|
29天前
|
存储 关系型数据库 MySQL
MySQL性能优化实践指南
【10月更文挑战第16天】MySQL性能优化实践指南
45 0
|
29天前
|
存储 关系型数据库 MySQL
MySQL性能优化指南
【10月更文挑战第16天】MySQL性能优化指南
35 0
|
2月前
|
存储 关系型数据库 MySQL
mysql-性能优化(一)
mysql-性能优化(一)
|
2月前
|
关系型数据库 MySQL 数据处理
针对MySQL亿级数据的高效插入策略与性能优化技巧
在处理MySQL亿级数据的高效插入和性能优化时,以上提到的策略和技巧可以显著提升数据处理速度,减少系统负担,并保持数据的稳定性和一致性。正确实施这些策略需要深入理解MySQL的工作原理和业务需求,以便做出最适合的配置调整。
343 6
|
1月前
|
存储 关系型数据库 MySQL
深度剖析:MySQL聚合函数 count(expr) 如何工作?如何选择?
本文详细探讨了MySQL中count(expr)函数的不同形式及其执行效率,包括count(*)、count(1)、count(主键)、count(非主键)等。通过对InnoDB和MyISAM引擎的对比分析,解释了它们在不同场景下的实现原理及性能差异。文章还通过实例演示了事务隔离级别对统计结果的影响,并提供了源码分析和总结建议。适合希望深入了解MySQL统计函数的开发者阅读。
55 0
|
2月前
|
SQL 存储 关系型数据库
深入 MySQL 的执行计划与性能优化
深入 MySQL 的执行计划与性能优化
39 0
|
3月前
|
存储 关系型数据库 MySQL
"深入探索MySQL临时表:性能优化利器,数据处理的灵活之选"
【8月更文挑战第9天】MySQL临时表专为存储临时数据设计,自动创建与删除,仅在当前会话中存在,有助于性能优化。它分为本地临时表和全局临时表(通过特定逻辑模拟)。创建语法类似于普通表,但加TEMPORARY或TEMP关键字。适用于性能优化、数据预处理和复杂查询,需注意内存占用和事务支持问题。合理使用可大幅提升查询效率。
212 2