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%’` 时索引无效

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
7天前
|
存储 自然语言处理 关系型数据库
MySQL高级篇——索引的创建与设计原则
索引的分类与使用、MySQL8.0索引新特性、适合创建索引的情况、不适合创建索引的情况
MySQL高级篇——索引的创建与设计原则
|
7天前
|
存储 SQL 关系型数据库
MySQL高级篇——索引失效的11种情况
索引优化思路、要尽量满足全值匹配、最佳左前缀法则、主键插入顺序尽量自增、计算、函数导致索引失效、类型转换(手动或自动)导致索引失效、范围条件右边的列索引失效、不等于符号导致索引失效、is not null、not like无法使用索引、左模糊查询导致索引失效、“OR”前后存在非索引列,导致索引失效、不同字符集导致索引失败,建议utf8mb4
MySQL高级篇——索引失效的11种情况
|
16天前
|
存储 关系型数据库 MySQL
MySQL基础:索引
MySQL中的索引是一种数据结构,能大幅提升数据库查询效率和减少I/O成本,类似于书的目录帮助快速定位内容。其优势包括提高检索效率和降低排序成本,但会占用空间并影响更新表的效率。鉴于查询远多于更新,索引仍被推荐使用。索引分为多种类型,如B+树和哈希索引,其中B+树因其较低的高度和稳定的查询开销成为常用选择。创建和删除索引需谨慎,以免影响性能。
40 4
MySQL基础:索引
|
7天前
|
存储 SQL 关系型数据库
【MySQL调优】如何进行MySQL调优?从参数、数据建模、索引、SQL语句等方向,三万字详细解读MySQL的性能优化方案(2024版)
MySQL调优主要分为三个步骤:监控报警、排查慢SQL、MySQL调优。 排查慢SQL:开启慢查询日志 、找出最慢的几条SQL、分析查询计划 。 MySQL调优: 基础优化:缓存优化、硬件优化、参数优化、定期清理垃圾、使用合适的存储引擎、读写分离、分库分表; 表设计优化:数据类型优化、冷热数据分表等。 索引优化:考虑索引失效的11个场景、遵循索引设计原则、连接查询优化、排序优化、深分页查询优化、覆盖索引、索引下推、用普通索引等。 SQL优化。
【MySQL调优】如何进行MySQL调优?从参数、数据建模、索引、SQL语句等方向,三万字详细解读MySQL的性能优化方案(2024版)
|
7天前
|
SQL 缓存 关系型数据库
MySQL高级篇——关联查询和子查询优化
左外连接:优先右表创建索引,连接字段类型要一致、内连接:驱动表由数据量和索引决定、 join语句原理、子查询优化:拆开查询或优化成连接查询
MySQL高级篇——关联查询和子查询优化
|
7天前
|
存储 缓存 关系型数据库
MySQL高级篇——存储引擎和索引
MyISAM:不支持外键和事务,表锁不适合高并发,只缓存索引,内存要求低,查询快MyISAM提供了大量的特性,包括全文索引、压缩、空间函数(GIS)等,但MyISAM不支持事务、行级锁、外键,有一个毫无疑问的缺陷就是崩溃后无法安全恢复。5.5之前默认的存储引擎优势是访问的速度快,对事务完整性没有要求或者以SELECT、INSERT为主的应用针对数据统计有额外的常数存储。故而 count(*) 的查询效率很高表名.frm 存储表结构;表名.MYD 存储数据 (MYData);
MySQL高级篇——存储引擎和索引
|
7天前
|
算法 关系型数据库 MySQL
MySQL高级篇——排序、分组、分页优化
排序优化建议、案例验证、范围查询时索引字段选择、filesort调优、双路排序和单路排序、分组优化、带排序的深分页优化
MySQL高级篇——排序、分组、分页优化
|
7天前
|
存储 关系型数据库 MySQL
MySQL高级篇——覆盖索引、前缀索引、索引下推、SQL优化、主键设计
覆盖索引、前缀索引、索引下推、SQL优化、EXISTS 和 IN 的区分、建议COUNT(*)或COUNT(1)、建议SELECT(字段)而不是SELECT(*)、LIMIT 1 对优化的影响、多使用COMMIT、主键设计、自增主键的缺点、淘宝订单号的主键设计、MySQL 8.0改造UUID为有序
MySQL高级篇——覆盖索引、前缀索引、索引下推、SQL优化、主键设计
|
18天前
|
监控 关系型数据库 MySQL
zabbix agent集成percona监控MySQL的插件实战案例
这篇文章是关于如何使用Percona监控插件集成Zabbix agent来监控MySQL的实战案例。
28 2
zabbix agent集成percona监控MySQL的插件实战案例
|
21天前
|
SQL 关系型数据库 MySQL
SQL Server、MySQL、PostgreSQL:主流数据库SQL语法异同比较——深入探讨数据类型、分页查询、表创建与数据插入、函数和索引等关键语法差异,为跨数据库开发提供实用指导
【8月更文挑战第31天】SQL Server、MySQL和PostgreSQL是当今最流行的关系型数据库管理系统,均使用SQL作为查询语言,但在语法和功能实现上存在差异。本文将比较它们在数据类型、分页查询、创建和插入数据以及函数和索引等方面的异同,帮助开发者更好地理解和使用这些数据库。尽管它们共用SQL语言,但每个系统都有独特的语法规则,了解这些差异有助于提升开发效率和项目成功率。
92 0