mysql count(*) count(1) count(id) 讲解 千万数据测试
程序员文章站
2022-04-09 19:40:36
...
数据100万增加到 1000万测试数据
环境 win10 8g内存 mysql5.7.27
mysql my.ini 配置 注意(innodb_flush_log_at_trx_commit=0)配置
[Client]
#设置3306端口
port = 3306
[mysqld]
#设置3306端口
port = 3306
# 设置mysql的安装目录
basedir=E:devmysql-5.7.27-winx64
# 设置mysql数据库的数据的存放目录
datadir=E:devmysql-5.7.27-winx64data
# 允许最大连接数
max_connections=200
# 服务端使用的字符集默认为8比特编码的latin1字符集
character-set-server=utf8
# 创建新表时将使用的默认存储引擎
default-storage-engine=INNODB
sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
# 插入数据时设置为0提高插入数据,插入数据完成修改为1
innodb_flush_log_at_trx_commit=0
[mysql]
# 设置mysql客户端默认字符集
default-character-set=utf8
1、创建表
CREATE TABLE `test_count` (
`id` bigint(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(60) DEFAULT NULL,
`age` int(3) DEFAULT NULL,
`create_time` datetime DEFAULT NULL,
`test1` varchar(100) DEFAULT NULL,
`test2` varchar(100) DEFAULT NULL,
`test3` varchar(100) DEFAULT NULL,
`test4` varchar(100) DEFAULT NULL,
`test5` varchar(100) DEFAULT NULL,
`test6` varchar(100) DEFAULT NULL,
`test10` varchar(100) DEFAULT NULL,
`test11` varchar(100) DEFAULT NULL,
`test12` varchar(100) DEFAULT NULL,
`test13` varchar(200) DEFAULT NULL,
`test14` varchar(200) DEFAULT NULL,
`test15` varchar(200) DEFAULT NULL,
`test16` varchar(200) DEFAULT NULL,
`test17` varchar(200) DEFAULT NULL,
`test18` varchar(200) DEFAULT NULL,
`test19` varchar(200) DEFAULT NULL,
`test20` varchar(200) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
2、创建存储过程
CREATE PROCEDURE `test_count_dure`(IN n int)
BEGIN
DECLARE i INT DEFAULT 1;
WHILE (i <= n ) DO
INSERT into test_count (name,age,create_time,test1,test2,test3,test4,test5,test6,test10,test11,test12,test13,test14,test15,test16,test17,test18,test19,test20 ) VALUEs
(i,100 ,now(),'213dfdsjfdasfdsdafdffsd',
'213dfdsjfdasfdsdafdffsd','213dfdsjfdasfdsdafdffsd','213dfdsjfdasfdsdafdffsd'
,'213dfdsjfdasfdsdafdffsd','213dfdsjfdasfdsdafdffsd','afdasfjdlajlfkalfdakfjdlajfkalfdasfd','afdasfjdlajlfkalfdakfjdlajfkalfdasfd','afdasfjdlajlfkalfdakfjdlajfkalfdasfd','afdasfjdlajlfkalfdakfjdlajfkalfdasfd','afdasfjdlajlfkalfdakfjdlajfkalfdasfd','afdasfjdlajlfkalfdakfjdlajfkalfdasfd','afdasfjdlajlfkalfdakfjdlajfkalfdasfd','afdasfjdlajlfkalfdakfjdlajfkalfdasfd','afdasfjdlajlfkalfdakfjdlajfkalfdasfd','afdasfjdlajlfkalfdakfjdlajfkalfdasfd','afdasfjdlajlfkalfdakfjdlajfkalfdasfd' );
set i=i+1;
END WHILE;
END
3、创建100万数据
CALL test_count_dure(1000000);
4、查看表占用磁盘大小
SELECT CONCAT(TRUNCATE(SUM(data_length)/1024/1024,2),'MB') AS data_size,
CONCAT(TRUNCATE(SUM(max_data_length)/1024/1024,2),'MB') AS max_data_size,
CONCAT(TRUNCATE(SUM(data_free)/1024/1024,2),'MB') AS data_free,
CONCAT(TRUNCATE(SUM(index_length)/1024/1024,2),'MB') AS index_size
FROM information_schema.tables WHERE TABLE_NAME = 'test_count';
5、执行count
count(1) count(*) count(1) 基本都在 8秒
强制使用 主键当做索引 时间还是 8秒
6、增加test_count 二级索引
给name 加一个普通索引
CREATE TABLE `test_count` (
`id` bigint(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(60) DEFAULT NULL,
`age` int(3) DEFAULT NULL,
`create_time` datetime DEFAULT NULL,
`test1` varchar(100) DEFAULT NULL,
`test2` varchar(100) DEFAULT NULL,
`test3` varchar(100) DEFAULT NULL,
`test4` varchar(100) DEFAULT NULL,
`test5` varchar(100) DEFAULT NULL,
`test6` varchar(100) DEFAULT NULL,
`test10` varchar(100) DEFAULT NULL,
`test11` varchar(100) DEFAULT NULL,
`test12` varchar(100) DEFAULT NULL,
`test13` varchar(200) DEFAULT NULL,
`test14` varchar(200) DEFAULT NULL,
`test15` varchar(200) DEFAULT NULL,
`test16` varchar(200) DEFAULT NULL,
`test17` varchar(200) DEFAULT NULL,
`test18` varchar(200) DEFAULT NULL,
`test19` varchar(200) DEFAULT NULL,
`test20` varchar(200) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `name` (`name`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
7、再次执行 count(1) count(*) count(1)
通过多次执行
count(*) count(1) 执行时间 在 0.2~0.3秒
count(id) 执行时间 在 0.3~0.4秒
通过explain 执行计划查看 使用的是二级索引 name索引 从而提高了查询速度
8、通过网上查询得知为什么加入一个二级索引速度就快了的原因
1)普通查询
普通查询使用主键索引是最快的,整体IO消耗也最少,使用二级索引时,由于先要根据二级索引定位到主健索引,再根据主健索引查询数据文件,IO消耗比主键索引大
2)统计查询
统计类需求,如COUNT之类的,反而使用二级索引比主键索引性能更高,因为MYSQL使用B+树,主健索引与数据共同存放在B+树叶子结点,如果使用主健索引进行统计时,每次都要扫描数据文件,当数据字段及行数较多时(数据文件较大),扫描时IO消耗较高。而二级索引只存放索引数据,索引文件比较小,不需要搜索数据文件,整体IO消耗低
count(*)=count(1)>=count(primarykey)>=count(非空字段)>=count(可为空字段)
数据增加1000万测试用例
首先看下数据磁盘占用空间 单表6G
test_count表增加字段
# 增加x1字段
ALTER TABLE `test_count`
ADD COLUMN `x1` char(1) NOT NULL DEFAULT 'a';
# 增加x1字段索引
ALTER TABLE `test_count`
ADD INDEX `x1` (`x1`) USING BTREE ;
查看count(* ) 3.7秒 3~4秒之间
查看count(1) 3.8秒 3~4秒之间
查看count (id) 4.058 秒 3.9~4 秒以上
查看表索引情况
mysql> show index from test_count;
+------------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+------------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| test_count | 0 | PRIMARY | 1 | id | A | 9441071 | NULL | NULL | | BTREE | | |
| test_count | 1 | name | 1 | name | A | 7791336 | NULL | NULL | YES | BTREE | | |
| test_count | 1 | x1 | 1 | x1 | A | 1 | NULL | NULL | | BTREE | | |
+------------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
3 rows in set
在上面测试的时候发现默认走的是name 索引,这次为什么走了x1 索引
mysql> 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 | x1 | 3 | NULL | 9606635 | 100 | Using index |
+----+-------------+------------+------------+-------+---------------+-----+---------+------+---------+----------+-------------+
1 row in set
查看强制使用 name当索引执行时间 15秒
x1 char(1) NO MUL a (char(1)长度)
name varchar(60) YES MUL (varchar(60)长度)
推荐阅读
-
mysql found_row()与row_count()实例讲解
-
select count()和select count(1)的区别和执行方式讲解
-
count(*) count(1) count(id) 区别
-
MySQL中count(*)、count(1)和count(col)的区别汇总
-
select count()和select count(1)的区别和执行方式讲解
-
MySQL count(1)、count(*)、count(字段)的区别
-
Column count doesn't match value count at row 1_MySQL
-
count(1),count(id)以及count(*)的区别
-
count(1),count(id)以及count(*)的区别
-
mysql found_row()与row_count()实例讲解