MySQL索引优化案例

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用系列 2核4GB
简介: 开发同学或多或少会遇到系统响应慢的问题,除了业务系统本身的问题外,常常会遇到SQL查询慢的问题,这篇文章结合实际案例分析MySQL InnoDB存储引擎的索引优化,这篇文章不会介绍B+树的知识点,如果需要了解聚集索引和辅助索引特点的同学可以参考这篇文章,这篇文章主要会介绍三星索引和ICP优化.

开发同学或多或少会遇到系统响应慢的问题,除了业务系统本身的问题外,常常会遇到SQL查询慢的问题,这篇文章结合实际案例分析MySQL InnoDB存储引擎的索引优化,这篇文章不会介绍B+树的知识点,如果需要了解聚集索引和辅助索引特点的同学可以参考这篇文章,这篇文章主要会介绍三星索引和ICP优化.

如何分析SQL性能
首先是查看MySQL的状态,系统是否正常,常用的几个命令如下:

显示状态信息(扩展show status like ‘XXX’)

Mysql> show status;

显示系统变量(扩展show variables like ‘XXX’)

Mysql> show variablesG;

显示InnoDB存储引擎的状态

Mysql> show engine innodb statusG;

查看当前SQL执行,包括执行状态、是否锁表等

Mysql> show processlist ;
第二步是找出系统有哪些慢查询SQL,这个要通过slowLog来查询,首先开启慢查询日志,然后在对应日志路径找到mysql-slow.log,相关命令如下所示:

检查是否开启慢查询日志

show variables like '%slow%';

如果没有开启,也可以在运行时动态开启这个参数

set global slow_query_log=ON;

设置慢查询记录查询耗时多长的SQL,这里设置成100毫秒

set long_query_time = 0.1;

这里休眠500毫秒试一下慢查询日志是否会记录

select sleep(0.5)
找到了慢SQL后比较常见的做法就是用explain命令分析SQL执行计划,查看SQL语句是否命中了索引,explain的用法可以参考MySQL 性能优化神器 Explain 使用分析,在优化过程中我们可能需要看到优化前后的查询时间对比,这时候可以打开profiling开关,查看某条SQL语句的执行耗时情况,分析是哪个步骤耗时较长,相关设置如下:

查看是否开启profiling

select @@profiling;

开profiling,注意测试完关闭该特性,否则耗费资源

set profiling=1;

查看所有记录profile的SQL

show profiles;

查看指定ID的SQL的详情

show profile for query 1;

测试完,关闭该特性

set profiling=0;
一个profiling例子:

profiling
三星索引优化策略
三星索引的策略是根据查询语句来建立联合索引,比如有这样一条SQL,SELECT GroupId,AddTime,Status from Order WHERE GroupId = 10010 ORDER BY AddTime,如何用三星索引来优化这条查询呢?

一星索引的核心就是利用索引来尽可能的过滤不必要的数据,减少数据处理的规模,对于RDBMS来说是极为关键的,比如说Order表有100万行数据,GroupId的过滤度(cardinality)是90%,Status的过滤度是0.1%,,如果没有建立索引,那要扫描100万行数据去找到GroupId等于10010的那一条数据,因此这里需要把GroupId作为联合索引的第一列,你是不是加了GroupId的索引就解决问题了呢,答案是否定的,虽然GroupId索引过滤了大部分数据,但由于select 后面有order by语句,而且这条SQL查询的不止GroupId这一个字段,不可避免要二次IO,而且还要在内存里进行一次filesort,explain的执行结果如下所示:
explain

从上图可以看到这条SQL使用了GroupId这个索引,但是Extra里面并不是直接Using Index,而是Using Index Condition,这意味着通过索引无法完成这条查询,存储引擎还是得读取对应的记录来完成查询,不过这里使用了ICP技术把GroupId这个索引下推到存储引擎层进行过滤,而且在内存中进行了一次filesort排序.
这里就要用到二星索引来优化filesort了,filesort一般出现在要排序的字段没有添加索引或者即使添加了索引但索引不是有顺序的情况下,需要在内存中进行一次排序然后再返回给客户端,如果数据量比较大的情况下排序的开销也挺大,二星索引基本的想法就是利用索引的有序性,消除orderby或者group by等需要排序的操作(备注:group by语句默认会对该字段排序),排序是非常消耗CPU资源的,大量的排序操作会把user cpu搞得很高,即使CPU吃得消,如果数据量比较大,需要排序的数据放不下内存的sort buffer,只能悲剧的和外存换进换出,性能下降的就不是一点两点了,这时候利用索引避免排序的优势就明显的体现出来了,这时候就需要建立GroupId和AddTime的联合索引了,当GroupId相等的时候联合索引的第二列AddTime就默认是有顺序的,这样就可以避免filesort,修改索引后的效果如下所示:
explain
从上面的图片可以看到filesort已经没有了,但是还是使用ICP而不是Using Index,这是因为我们Select的字段还包含Status这一列,而这一列不在联合索引中,因此存储引擎还是需要读取该行记录来获取Status的值,这时候三星索引就派上用场了, 在索引中额外添加要查询的列Status,这就是所谓的索引覆盖,即在索引的叶子节点就能够读到查询SQL所需要的所有信息,而不需要回原表去查询,如下图所示:
explain

这里的Extra列显示是Using Where,Using Index,意思是数据是从索引里面取,不需要回表,Using where表示需要根据其他列来过滤数据,从key_len列也看出索引的长度为5,表示只用了GroupId这个索引,索引长度的计算可以参考这篇文章
除了order by,group by的字段适合作为二星索引外,范围查询(包括between and)也适合,但是当order by和范围查询同时存在时,就需要根据实际情况作出取舍了,看是filesort的开销比较大还是范围查询的开销大

关于三星索引,并不是说要把select语句里所有的查询字段都加到索引列,索引字段太多会给数据库带来很大的开销,特别是会影响DML语句的性能,而且还要结合cardinality字段来分析,有些字段的取值范围很小,选择度很低,不适合作为索引.

ICP技术介绍
前面的章节多次提到了ICP技术,那到底什么是ICP呢,在介绍ICP之前我们先来看一下SQL语句的where条件提取规则:所有SQL的where条件,都可以归纳为3大类:Index Key,Index Filter和Table Filter,下面的介绍以CREATE TABLE Order (GroupId int(11) DEFAULT NULL, AddTime datetime DEFAULT NULL, Status int(11) DEFAULT NULL,KEY IX_GroupId (GroupId), KEY IX_AddTime (AddTime)) ENGINE=InnoDB这个表结构为例,假设我们分别单独建立了groupId和AddTime的索引,假设我们要执行这样一条SQLselect groupid,addtime,status from Order where groupid < 10010 and addTime > '1970' and status = 0;,那SQL执行计划会是怎样呢?

Index Key
Index Key只是用来定位索引的起止范围,因此只在索引第一次搜索时使用,一次判断即可;

从起始范围之后读到的每一条索引记录,均需要判断是否在Index Key的范围内,以上面的SQL为例,Index Key就是GroupId,它的范围是GroupId < 10010.

Index Filter
用于过滤索引查询范围中不满足查询条件的记录,因此对于索引范围中的每一条记录,均需要与Index Filter进行对比,若不满足Index Filter则直接丢弃,继续读取索引下一条记录,在前面的SQL中,Index Filter就是AddTime > '1970',MySQL会把这个过滤条件下推到存储引擎层,ICP就是Index Condition Push Down,不管是Index Key还是Index Filter对应的列必须添加了索引.

Table Filter
Table Filter是Where条件最后一道防线,用于过滤通过前面索引的层层考验的记录,此时的记录已经满足了Index Key的范围,并且符合Index Filter的条件,存储引擎通过回表读取了完整的记录,判断整条记录是否满足Table Filter中的查询条件,同样的,若不满足,跳过当前记录,继续读取索引的下一条记录,若满足,则返回记录,此记录满足了where的所有条件,可以返回给前端用户.还是回到前面那条SQL,Where条件中的Status=0就是Table Filter,因为Status列并没有添加索引,所以只能通过回表读取该字段来过滤.

我们来总结下ICP的过程,MySQL把Index Filter条件AddTime>'1970'下推到存储引擎,存储引擎根据AddTime索引来过滤掉不符合where条件AddTime>'1970'的记录,然后把最终数据返回给MySQL Server,MySQL Server再根据where条件status=0来做最后的过滤,最后把数据返回给用户,这个过程减少了二次回表的次数,有效减少了磁盘IO.最后我们来看下explain语句的结果:

Table Filter
其他优化建议
整型数据比起字符,处理开销更小,在MySQL中,应该用内置的日期和时间数据类型,而不是用字符串来存储时间;
尽量指定列为NOT NULL,除非你想存储NULL,在MySQL中,含有空值的列很难进行查询优化,因为它们使得索引、索引的统计信息以及比较运算更加复杂,你应该用0、一个特殊的值或者一个空串代替空值;
当结果集只需要一行数据时使用LIMIT 1
避免SELECT *,始终指定你需要的列
使用连接(JOIN)来代替子查询(Sub-Queries),使用Join来代理大的分页语句,比如select ... Limit 1000000,10 这条语句MySQL要根据查询条件找到符合的100万条记录,然后删掉再读取后10条记录,应该根据Where条件把主键查出来,然后根据主键去访问数据,比如select * from Order o inner join (select Id from Order where ...) t where o.id=t.id limit 1000000,10;
where子句的查询条件里有!=,MySQL将无法使用索引
使用LIKE进行搜索匹配的时候,这样索引是有效的:select * from Order where name like ‘xxx%’,而like ‘%xxx%’` 时索引无效

相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
9天前
|
关系型数据库 MySQL Linux
MySQL原理简介—6.简单的生产优化案例
本文介绍了数据库和存储系统的几个主题: 1. **MySQL日志的顺序写和数据文件的随机读指标**:解释了磁盘随机读和顺序写的原理及对数据库性能的影响。 2. **Linux存储系统软件层原理及IO调度优化原理**:解析了Linux存储系统的分层架构,包括VFS、Page Cache、IO调度等,并推荐使用deadline算法优化IO调度。 3. **数据库服务器使用的RAID存储架构**:介绍了RAID技术的基本概念及其如何通过多磁盘阵列提高存储容量和数据冗余性。 4. **数据库Too many connections故障定位**:分析了MySQL连接数限制问题的原因及解决方法。
|
4天前
|
缓存 算法 关系型数据库
MySQL底层概述—8.JOIN排序索引优化
本文主要介绍了MySQL中几种关键的优化技术和概念,包括Join算法原理、IN和EXISTS函数的使用场景、索引排序与额外排序(Using filesort)的区别及优化方法、以及单表和多表查询的索引优化策略。
MySQL底层概述—8.JOIN排序索引优化
|
5天前
|
SQL 关系型数据库 MySQL
MySQL底层概述—7.优化原则及慢查询
本文主要介绍了:Explain概述、Explain详解、索引优化数据准备、索引优化原则详解、慢查询设置与测试、慢查询SQL优化思路
MySQL底层概述—7.优化原则及慢查询
|
7天前
|
SQL 存储 关系型数据库
MySQL原理简介—9.MySQL索引原理
本文详细介绍了MySQL索引的设计与使用原则,涵盖磁盘数据页的存储结构、页分裂机制、主键索引设计及查询过程、聚簇索引和二级索引的原理、B+树索引的维护、联合索引的使用规则、SQL排序和分组时如何利用索引、回表查询对性能的影响以及索引覆盖的概念。此外还讨论了索引设计的案例,包括如何处理where筛选和order by排序之间的冲突、低基数字段的处理方式、范围查询字段的位置安排,以及通过辅助索引来优化特定查询场景。总结了设计索引的原则,如尽量包含where、order by、group by中的字段,选择离散度高的字段作为索引,限制索引数量,并针对频繁查询的低基数字段进行特殊处理等。
MySQL原理简介—9.MySQL索引原理
|
5天前
|
存储 缓存 关系型数据库
MySQL底层概述—5.InnoDB参数优化
本文介绍了MySQL数据库中与内存、日志和IO线程相关的参数优化,旨在提升数据库性能。主要内容包括: 1. 内存相关参数优化:缓冲池内存大小配置、配置多个Buffer Pool实例、Chunk大小配置、InnoDB缓存性能评估、Page管理相关参数、Change Buffer相关参数优化。 2. 日志相关参数优化:日志缓冲区配置、日志文件参数优化。 3. IO线程相关参数优化: 查询缓存参数、脏页刷盘参数、LRU链表参数、脏页刷盘相关参数。
MySQL底层概述—5.InnoDB参数优化
|
5天前
|
存储 关系型数据库 MySQL
MySQL底层概述—6.索引原理
本文详细回顾了:索引原理、二叉查找树、平衡二叉树(AVL树)、红黑树、B-Tree、B+Tree、Hash索引、聚簇索引与非聚簇索引。
MySQL底层概述—6.索引原理
|
6天前
|
存储 SQL 关系型数据库
服务器数据恢复—云服务器上mysql数据库数据恢复案例
某ECS网站服务器,linux操作系统+mysql数据库。mysql数据库采用innodb作为默认存储引擎。 在执行数据库版本更新测试时,操作人员误误将在本来应该在测试库执行的sql脚本在生产库上执行,导致生产库上部分表被truncate,还有部分表中少量数据被delete。
46 25
|
7天前
|
关系型数据库 MySQL 数据库
从MySQL优化到脑力健康:技术人与效率的双重提升
聊到效率这个事,大家应该都挺有感触的吧。 不管是技术优化还是个人状态调整,怎么能更快、更省力地完成事情,都是我们每天要琢磨的事。
56 23
|
7天前
|
SQL 关系型数据库 MySQL
MySQL原理简介—11.优化案例介绍
本文介绍了四个SQL性能优化案例,涵盖不同场景下的问题分析与解决方案: 1. 禁止或改写SQL避免自动半连接优化。 2. 指定索引避免按聚簇索引全表扫描大表。 3. 按聚簇索引扫描小表减少回表次数。 4. 避免产生长事务长时间执行。
|
24天前
|
关系型数据库 MySQL 数据库连接
数据库连接工具连接mysql提示:“Host ‘172.23.0.1‘ is not allowed to connect to this MySQL server“
docker-compose部署mysql8服务后,连接时提示不允许连接问题解决

推荐镜像

更多