关于mysql的innodb 800w+数据不带条件count性能优化的思考
wptr33 2024-12-28 15:58 36 浏览
关于mysql的innodb 800w+数据不带条件count性能优化的思考
1、查看mysql版本
mysql> show variables like '%version%';
+-------------------------+------------------------------+
| Variable_name | Value |
+-------------------------+------------------------------+
| innodb_version | 5.7.32 |
| protocol_version | 10 |
| slave_type_conversions | |
| tls_version | TLSv1,TLSv1.1,TLSv1.2 |
| version | 5.7.32 |
| version_comment | MySQL Community Server (GPL) |
| version_compile_machine | x86_64 |
| version_compile_os | Win64 |
+-------------------------+------------------------------+
2、建表并初始化数据
2.1、建表语句如下
mysql> desc user;
mysql> show create table user;
CREATE TABLE `user` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
`birthday` date NOT NULL,
`no` int(11) NOT NULL DEFAULT '0',
`remark` varchar(255) NOT NULL,
`modify_date` datetime NOT NULL ON UPDATE CURRENT_TIMESTAMP,
`create_date` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
2.2、初始化500w+数据
insert into user(name,birthday,no,remark,modify_date,create_date) values("test-user-name",'2000-01-01',0,"测试count性能",'2019-01-01','2019-01-01');
反复执行以下语句直至数据量超过500w+
insert into user(name,birthday,no,remark,modify_date,create_date) select name,birthday,no,remark,modify_date,create_date from user ;
执行一些更新操作
update user set no=id+1000000;
3、开启profiling参数并查看耗时
3.1、开启profiling参数
mysql> show variables like '%profiling%'; 查看配置情况
mysql> set profiling=1; 开启配置
3.2、查看count语句执行耗时
使用count(*)或count(1)耗时均超过4秒
mysql> select count(*) from user;
+----------+
| count(*) |
+----------+
| 8388608 |
+----------+
mysql> show profiles;
+----------+------------+-----------------------------------+
| Query_ID | Duration | Query |
+----------+------------+-----------------------------------+
| 1 | 0.00172175 | show variables like '%profiling%' |
| 2 | 4.09369675 | select count(*) from user |
+----------+------------+-----------------------------------+
mysql> show profile for query 2;
+----------------------+----------+
| Status | Duration |
+----------------------+----------+
| starting | 4.7E-5 |
| checking permissions | 4E-6 |
| Opening tables | 1.4E-5 |
| init | 8E-6 |
| System lock | 5E-6 |
| optimizing | 3E-6 |
| statistics | 9E-6 |
| preparing | 7E-6 |
| executing | 2E-6 |
| Sending data | 4.093506 |
| end | 8E-6 |
| query end | 2E-5 |
| closing tables | 7E-6 |
| freeing items | 4.8E-5 |
| cleaning up | 9E-6 |
+----------------------+----------+
在user表没有辅助索引,仅有一个主键索引的情况下,可以看到耗时超过4秒
3.3、创建no字段的辅助索引再进行count统计
create index index_no on user(no);
3.4、查看当前索引情况
mysql> show index from user;
3.5、增加辅助索引后再次执行count可以看到查询速度有明显的提升
可通过show profiles查看最新的queryid,观察实际的耗时
4、通过explain执行计划分析一下有辅助索引和无辅助索引的区别
mysql> explain select count(*) from user;
+----+-------------+-------+------------+-------+---------------+----------+---------+------+---------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+----------+---------+------+---------+----------+-------------+
| 1 | SIMPLE | user | NULL | index | NULL | index_no | 4 | NULL | 8159175 | 100 | Using index |
+----+-------------+-------+------------+-------+---------------+----------+---------+------+---------+----------+-------------+
mysql> drop index index_no on user;
mysql> explain select count(*) from user;
+----+-------------+-------+------------+-------+---------------+---------+---------+------+---------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+---------+----------+-------------+
| 1 | SIMPLE | user | NULL | index | NULL | PRIMARY | 4 | NULL | 8159175 | 100 | Using index |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+---------+----------+-------------+
通过explain执行计划可以看到count统计查询在有辅助索引时,选择了走辅助索引,没有时选择了PRIMARY主键索引,从结果可以看到使用了主键索引反而比较慢,这是为什么呢?通常mysql普通的数据检索时主键索引会比普通索引快,原因是主键索引不需要回表,而count是什么原因会造成有如此大的差异呢?
5、继续通过optimizer trace跟踪优化器过程
5.1、查看optimizer_trace状态
mysql> show variables like '%optimizer_trace%';
+------------------------------+----------------------------------------------------------------------------+
| Variable_name | Value |
+------------------------------+----------------------------------------------------------------------------+
| optimizer_trace | enabled=off,one_line=off |
| optimizer_trace_features | greedy_search=on,range_optimizer=on,dynamic_range=on,repeated_subselect=on |
| optimizer_trace_limit | 1 |
| optimizer_trace_max_mem_size | 16384 |
| optimizer_trace_offset | -1 |
+------------------------------+----------------------------------------------------------------------------+
5.2、开启并查看执行情况
- 开启optimizer_trace
mysql> show variables like '%optimizer_trace%'; 查看参数状态
mysql> set optimizer_trace="enabled=on";
mysql> set end_markers_in_json=on;
- 在有索引与无索引情况下执行count查询后均使用以下查询语句获取优化器优化过程
SELECT * FROM information_schema.OPTIMIZER_TRACE;
可以观察到在有辅助索引及无辅助索引index_no的两种情况下,优化器的执行过程一样,并没有差异,没有体现出性能的差异,不过在optimizer_trace结果中可以看到是scan;
"considered_execution_plans": [
{
"plan_prefix": [
],
"table": "`user`",
"best_access_path": {
"considered_access_paths": [
{
"rows_to_scan": 8159175,
"access_type": "scan",
"resulting_rows": 8.16e6,
"cost": 1.67e6,
"chosen": true
}
]
},
"condition_filtering_pct": 100,
"rows_for_plan": 8.16e6,
"cost_for_plan": 1.67e6,
"chosen": true
}
]
6、既然都是scan那应该就应该是和本身的结构有关了
6.1、聚簇索引(clustered index)和非聚簇索引(secondary index)的区别
innodb的clustered index是把primary key以及row data保存在一起的,而secondary index则是单独存放,然后有个指针指向primary key,所以二级索引树比主键索引树小。因此优化器基于成本的考虑,优先选择的是二级索引。
6.2、验证
- 创建新表user_no仅保留id及no字段
CREATE TABLE `user_no` (
`id` int(10) unsigned NOT NULL,
`no` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `index_no` (`no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
- 插入所有数据
insert into user_no select id,no from user
基于上面的结论,如果表的主键索引树与二级索引树差别不大的话应该可以获得相近的结果,默认count(*)时会选择二级索引(辅助索引)index_no,可以通过explain检查
mysql> explain select count(*) from user_no;
mysql> explain select count(*) from user_no force index(primary);
- 开启profiling参数并执行统计,查看耗时的差异
将分别统计user表(此表无辅助索引)、user_no表的两种情况(默认走辅助索引index_no以及主键索引)
mysql> set profiling=1;
mysql> select count(*) from user_no;
mysql> select count(*) from user_no force index(primary);
mysql> show profiles;
+----------+------------+---------------------------------------------------+
| Query_ID | Duration | Query |
+----------+------------+---------------------------------------------------+
| 1 | 4.862296 | select count(*) from user |
| 2 | 1.8102375 | select count(*) from user_no |
| 3 | 1.98250375 | select count(*) from user_no force index(primary) |
+----------+------------+---------------------------------------------------+
可以看到当表user_no只剩下2个字段时,使用主键索引或辅助索引时略有差异但并不大,按照这个结论,尝试在大表(较多字段)中使用了like操作,同样能够得到不错的性能提升select id from user force index(index_name) where name like '%33D0EB9%' ;
不同的机器性能或不同的mysql版本可能表现不一样
相关推荐
- MySQL进阶五之自动读写分离mysql-proxy
-
自动读写分离目前,大量现网用户的业务场景中存在读多写少、业务负载无法预测等情况,在有大量读请求的应用场景下,单个实例可能无法承受读取压力,甚至会对业务产生影响。为了实现读取能力的弹性扩展,分担数据库压...
- 3分钟短文 | Laravel SQL筛选两个日期之间的记录,怎么写?
-
引言今天说一个细分的需求,在模型中,或者使用laravel提供的EloquentORM功能,构造查询语句时,返回位于两个指定的日期之间的条目。应该怎么写?本文通过几个例子,为大家梳理一下。学习时...
- 一文由浅入深带你完全掌握MySQL的锁机制原理与应用
-
本文将跟大家聊聊InnoDB的锁。本文比较长,包括一条SQL是如何加锁的,一些加锁规则、如何分析和解决死锁问题等内容,建议耐心读完,肯定对大家有帮助的。为什么需要加锁呢?...
- 验证Mysql中联合索引的最左匹配原则
-
后端面试中一定是必问mysql的,在以往的面试中好几个面试官都反馈我Mysql基础不行,今天来着重复习一下自己的弱点知识。在Mysql调优中索引优化又是非常重要的方法,不管公司的大小只要后端项目中用到...
- MySQL索引解析(联合索引/最左前缀/覆盖索引/索引下推)
-
目录1.索引基础...
- 你会看 MySQL 的执行计划(EXPLAIN)吗?
-
SQL执行太慢怎么办?我们通常会使用EXPLAIN命令来查看SQL的执行计划,然后根据执行计划找出问题所在并进行优化。用法简介...
- MySQL 从入门到精通(四)之索引结构
-
索引概述索引(index),是帮助MySQL高效获取数据的数据结构(有序),在数据之外,数据库系统还维护者满足特定查询算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构...
- mysql总结——面试中最常问到的知识点
-
mysql作为开源数据库中的榜一大哥,一直是面试官们考察的重中之重。今天,我们来总结一下mysql的知识点,供大家复习参照,看完这些知识点,再加上一些边角细节,基本上能够应付大多mysql相关面试了(...
- mysql总结——面试中最常问到的知识点(2)
-
首先我们回顾一下上篇内容,主要复习了索引,事务,锁,以及SQL优化的工具。本篇文章接着写后面的内容。性能优化索引优化,SQL中索引的相关优化主要有以下几个方面:最好是全匹配。如果是联合索引的话,遵循最...
- MySQL基础全知全解!超详细无废话!轻松上手~
-
本期内容提醒:全篇2300+字,篇幅较长,可搭配饭菜一同“食”用,全篇无废话(除了这句),干货满满,可收藏供后期反复观看。注:MySQL中语法不区分大小写,本篇中...
- 深入剖析 MySQL 中的锁机制原理_mysql 锁详解
-
在互联网软件开发领域,MySQL作为一款广泛应用的关系型数据库管理系统,其锁机制在保障数据一致性和实现并发控制方面扮演着举足轻重的角色。对于互联网软件开发人员而言,深入理解MySQL的锁机制原理...
- Java 与 MySQL 性能优化:MySQL分区表设计与性能优化全解析
-
引言在数据库管理领域,随着数据量的不断增长,如何高效地管理和操作数据成为了一个关键问题。MySQL分区表作为一种有效的数据管理技术,能够将大型表划分为多个更小、更易管理的分区,从而提升数据库的性能和可...
- MySQL基础篇:DQL数据查询操作_mysql 查
-
一、基础查询DQL基础查询语法SELECT字段列表FROM表名列表WHERE条件列表GROUPBY分组字段列表HAVING分组后条件列表ORDERBY排序字段列表LIMIT...
- MySql:索引的基本使用_mysql索引的使用和原理
-
一、索引基础概念1.什么是索引?索引是数据库表的特殊数据结构(通常是B+树),用于...
- 一周热门
-
-
C# 13 和 .NET 9 全知道 :13 使用 ASP.NET Core 构建网站 (1)
-
程序员的开源月刊《HelloGitHub》第 71 期
-
详细介绍一下Redis的Watch机制,可以利用Watch机制来做什么?
-
假如有100W个用户抢一张票,除了负载均衡办法,怎么支持高并发?
-
如何将AI助手接入微信(打开ai手机助手)
-
Java面试必考问题:什么是乐观锁与悲观锁
-
SparkSQL——DataFrame的创建与使用
-
redission YYDS spring boot redission 使用
-
一文带你了解Redis与Memcached? redis与memcached的区别
-
如何利用Redis进行事务处理呢? 如何利用redis进行事务处理呢英文
-
- 最近发表
- 标签列表
-
- git pull (33)
- git fetch (35)
- mysql insert (35)
- mysql distinct (37)
- concat_ws (36)
- java continue (36)
- jenkins官网 (37)
- mysql 子查询 (37)
- python元组 (33)
- mybatis 分页 (35)
- vba split (37)
- redis watch (34)
- python list sort (37)
- nvarchar2 (34)
- mysql not null (36)
- hmset (35)
- python telnet (35)
- python readlines() 方法 (36)
- munmap (35)
- docker network create (35)
- redis 集合 (37)
- python sftp (37)
- setpriority (34)
- c语言 switch (34)
- git commit (34)