加入收藏 | 设为首页 | 会员中心 | 我要投稿 岳阳站长网 (https://www.0730zz.com.cn/)- 科技、建站、数据库平台、数据湖、视觉智能!
当前位置: 首页 > 云计算 > 正文

MySQL之COUNT性能到底如何?

发布时间:2022-08-02 11:13:51 所属栏目:云计算 来源:互联网
导读:在实际开发过程中,统计一个表的数据量是经常遇到的需求,用来统计数据库表的行数都会使用COUNT(*),COUNT(1)或者COUNT(字段),但是表中的记录越来越多,使用COUNT(*)也会变得越来越慢,本文我们就来分析一下COUNT的性能到底如何。 1.COUNT(1)、COUNT(*)与CO
  在实际开发过程中,统计一个表的数据量是经常遇到的需求,用来统计数据库表的行数都会使用COUNT(*),COUNT(1)或者COUNT(字段),但是表中的记录越来越多,使用COUNT(*)也会变得越来越慢,本文我们就来分析一下COUNT的性能到底如何。
 
  1.COUNT(1)、COUNT(*)与COUNT(字段)哪个更快?
  执行效果:
 
  COUNT(*)​MySQL 对COUNT(*)​进行了优化,COUNT(*)直接扫描主键索引记录,并不会把全部字段取出来,直接按行累加。
  COUNT(1)InnoDB引擎遍历整张表,但不取值,server 层对于返回的每一行,放一个数字“1”进去,按行累加。
  COUNT(字段)如果这个“字段”是定义为NOT NULL,那么InnoDB 引擎会一行行地从记录里面读出这个字段,server 层判断不能为NULL,按行累加;如果这个“字段”定义允许为NULL,那么InnoDB 引擎会一行行地从记录里面读出这个字段,然后把值取出来再判断一下,不是 NULL才累加。
  实验分析
  本文测试使用的环境:
  复制
  [root@zhyno1 ~]# cat /etc/system-release
  CentOS Linux release 7.9.2009 (Core)
 
  [root@zhyno1 ~]# uname -a
  Linux zhyno1 3.10.0-1160.62.1.el7.x86_64 #1 SMP Tue Apr 5 16:57:59 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
  1.
  2.
  3.
  4.
  5.
  测试数据库采用的是(存储引擎采用InnoDB,其它参数默认):
  复制
  (Mon Jul 25 09:41:39 2022)[root@GreatSQL][(none)]>select version();
  +-----------+
  | version() |
  +-----------+
  | 8.0.25-16 |
  +-----------+
  1 row in set (0.00 sec)
  1.
  2.
  3.
  4.
  5.
  6.
  7.
  实验开始:
 
  复制
  #首先我们创建一个实验表
 
  CREATE TABLE test_count (
    `id` int(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
    `name` varchar(20) NOT NULL,
    `salary` int(1) NOT NULL,
    KEY `idx_salary` (`salary`)
  ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
  #插入1000W条数据
  DELIMITER //
  CREATE PROCEDURE insert_1000w()
  BEGIN
      DECLARE i INT;
      SET i=1;
      WHILE i<=10000000 DO
          INSERT INTO test_count(name,salary) VALUES('KAiTO',1);
          SET i=i+1;
      END WHILE;
  END//
  DELIMITER ;
 
  #执行存储过程
  call insert_1000w();
  1.
  2.
  3.
  4.
  5.
  6.
  7.
  8.
  9.
  10.
  11.
  12.
  13.
  14.
  15.
  16.
  17.
  18.
  19.
  20.
  21.
  22.
  23.
  24.
  接下来我们分别来实验一下:
 
  COUNT(1)花费了4.19秒
  复制
  (Sat Jul 23 22:56:04 2022)[root@GreatSQL][test]>select count(1) from test_count;
  +----------+
  | count(1) |
  +----------+
  | 10000000 |
  +----------+
  1 row in set (4.19 sec)
  1.
  2.
  3.
  4.
  5.
  6.
  7.
  COUNT(*)花费了4.16秒
  复制
  (Sat Jul 23 22:57:41 2022)[root@GreatSQL][test]>select count(*) from test_count;
  +----------+
  | count(*) |
  +----------+
  | 10000000 |
  +----------+
  1 row in set (4.16 sec)
  1.
  2.
  3.
  4.
  5.
  6.
  7.
  COUNT(字段)花费了4.23秒
  复制
  (Sat Jul 23 22:58:56 2022)[root@GreatSQL][test]>select count(id) from test_count;
  +-----------+
  | count(id) |
  +-----------+
  |  10000000 |
  +-----------+
  1 row in set (4.23 sec)
  1.
  2.
  3.
  4.
  5.
  6.
  7.
  我们可以再来测试一下执行计划
 
  COUNT(*)
  复制
  (Sat Jul 23 22:59:16 2022)[root@GreatSQL][test]>explain select count(*) from test_count;
  +----+-------------+------------+------------+-------+---------------+------------+---------+------+---------+----------+-------------+
  | id | select_type | table      | partitions | type  | possible_keys | key        | key_len | ref  | rows    | filtered | Extra       |
  +----+-------------+------------+------------+-------+---------------+------------+---------+------+---------+----------+-------------+
  |  1 | SIMPLE      | test_count | NULL       | index | NULL          | idx_salary | 4       | NULL | 9980612 |   100.00 | Using index |
  +----+-------------+------------+------------+-------+---------------+------------+---------+------+---------+----------+-------------+
  1 row in set, 1 warning (0.01 sec)
 
  (Sat Jul 23 22:59:48 2022)[root@GreatSQL][test]>show warnings;
  +-------+------+-----------------------------------------------------------------------+
  | Level | Code | Message                                                               |
  +-------+------+-----------------------------------------------------------------------+
  | Note  | 1003 | /* select#1 */ select count(0) AS `count(*)` from `test`.`test_count` |
  +-------+------+-----------------------------------------------------------------------+
  1 row in set (0.00 sec)
  1.
  2.
  3.
  4.
  5.
  6.
  7.
  8.
  9.
  10.
  11.
  12.
  13.
  14.
  15.
  COUNT(1)
  复制
  (Sat Jul 23 23:12:45 2022)[root@GreatSQL][test]>explain select count(1) from test_count;
  +----+-------------+------------+------------+-------+---------------+------------+---------+------+---------+----------+-------------+
  | id | select_type | table      | partitions | type  | possible_keys | key        | key_len | ref  | rows    | filtered | Extra       |
  +----+-------------+------------+------------+-------+---------------+------------+---------+------+---------+----------+-------------+
  |  1 | SIMPLE      | test_count | NULL       | index | NULL          | idx_salary | 4       | NULL | 9980612 |   100.00 | Using index |
  +----+-------------+------------+------------+-------+---------------+------------+---------+------+---------+----------+-------------+
  1 row in set, 1 warning (0.00 sec)
 
  (Sat Jul 23 23:13:02 2022)[root@GreatSQL][test]>show warnings;
  +-------+------+-----------------------------------------------------------------------+
  | Level | Code | Message                                                               |
  +-------+------+-----------------------------------------------------------------------+
  | Note  | 1003 | /* select#1 */ select count(1) AS `count(1)` from `test`.`test_count` |
  +-------+------+-----------------------------------------------------------------------+
  1 row in set (0.00 sec)
  1.
  2.
  3.
  4.
  5.
  6.
  7.
  8.
  9.
  10.
  11.
  12.
  13.
  14.
  15.
  COUNT(字段)
  复制
  (Sat Jul 23 23:13:14 2022)[root@GreatSQL][test]>explain select count(id) from test_count;
  +----+-------------+------------+------------+-------+---------------+------------+---------+------+---------+----------+-------------+
  | id | select_type | table      | partitions | type  | possible_keys | key        | key_len | ref  | rows    | filtered | Extra       |
  +----+-------------+------------+------------+-------+---------------+------------+---------+------+---------+----------+-------------+
  |  1 | SIMPLE      | test_count | NULL       | index | NULL          | idx_salary | 4       | NULL | 9980612 |   100.00 | Using index |
  +----+-------------+------------+------------+-------+---------------+------------+---------+------+---------+----------+-------------+
  1 row in set, 1 warning (0.00 sec)
 
  (Sat Jul 23 23:13:29 2022)[root@GreatSQL][test]>show warnings;
  +-------+------+-----------------------------------------------------------------------------------------------+
  | Level | Code | Message                                                                                       |
  +-------+------+-----------------------------------------------------------------------------------------------+
  | Note  | 1003 | /* select#1 */ select count(`test`.`test_count`.`id`) AS `count(id)` from `test`.`test_count` |
  +-------+------+-----------------------------------------------------------------------------------------------+
  1 row in set (0.00 sec)
  1.
  2.
  3.
  4.
  5.
  6.
  7.
  8.
  9.
  10.
  11.
  12.
  13.
  14.
  15.
  需要注意的是COUNT里如果是非主键字段的话
  复制
  (Tue Jul 26 14:01:57 2022)[root@GreatSQL][test]>explain select count(name) from test_count where id <100 ;
  +----+-------------+------------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
  | id | select_type | table      | partitions | type  | possible_keys | key     | key_len | ref  | rows | filtered | Extra       |
  +----+-------------+------------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
  |  1 | SIMPLE      | test_count | NULL       | range | PRIMARY       | PRIMARY | 4       | NULL |   99 |   100.00 | Using where |
  +----+-------------+------------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
  1 row in set, 1 warning (0.00 sec)
  1.
  2.
  3.
  4.
  5.
  6.
  7.
  实验结果
  从上面的实验我们可以得出,COUNT(*)​和COUNT(1)​是最快的,其次是COUNT(id)。
  COUNT(*)​被MySQL查询优化器改写成了COUNT(0),并选择了idx_salary索引。
  COUNT(1)​和COUNT(id)都选择了idx_salary索引。

(编辑:岳阳站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    热点阅读