开发者学堂课程【云数据库优化经典案例:索引优化】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/67/detail/1158
索引优化
内容介绍:
一、无索引案例
二、隐式转换案例
云数据库优化十大经典案例,会对日常工作有很大帮助。
一、无索引案例
背景,用户反馈他的系统打开缓慢,同时数据库收到一个报警 CPU 到100%了,这个时候需要去排查一下到底是什么导致了数据库 CPU 100%,所以出现这个问题的时候很多用户都不知道怎么去排查,数据库 CPU 100%重点要去关注下数据库中它的慢 sql,数据库当时堆积的正在运行的 sql 是怎样的,所以此时通过 super csdiste 或者查看 iDste 控制台慢 SQL 后,可以发现大量的 sql 执行超过了两秒,这个 sql 其实比较简单,是根据一个用户表可能是根据他的手机号一查,查他的 uid, 这个时候发现慢 SQL,我们要去分析这个 sql 为什么这么慢?所以此时要去看一下它的执行计划,通过 explain 加 mysql 去看这个 sql 它的执行计划,所以我们可以看到这个 sql 它的执行计划是 ALL,这里 type 等于 ALL,表示全盘扫描,那这个 sql 执行一次的时候它需要扫描将近70多万的数据,所以可以想象一下如果这个系统频繁的调用 sql, 那么这个 sql 就容易出现堆积。所以数据库 CPU 100%的问题基本上可以定位于这个慢 sql 它的执行时间变慢了导致前端请求大量进来的时候数据库出现了堆积,这个时候要去解决慢 sql,看一下如何去解决这个 sql, 看到这个慢 sql 的第一反应是这个 sql 是不是有索引,在 user 表上是不是有 mo 字段的一个索引。
看表结构,这个 user 表是否有 mo 字段的索引,它有主键 uid,还有一个 email, 还有一个 pid 的索引,可以看到表上有三个索引,但是都没有 mo 质量的索引,所以要去考虑是否要在 mo 字段上加一个索引,加索引要去考虑过滤性,因为假如说这个字段是一个过滤性非常差的一个字段,那即使在质量上加了索引,有可能优化器它也不会去使用,所以在加索引之前,一定要去验证字段的过滤性
验证这个字段的过滤性,去查一下这个表符合条件的数据有多少,那我们可以看到 mo 字段最终满足条件的数据集有0行,可以得出这个结论,根据条件去查的数据是非常少的,那此时在这个字段上加一个索引它的效率是非常高的,这里还有一个方法就是可以通过 distinct 这个 mo 字段以及看着 distinct 这个 mo 字段不同的 个数及表的总个数去判断字段的过滤性,那可以看到就在这个表上加一个 mo 字段的索引,再去看它的执行时间,加索引“alter table user add index ind_mo”,执行时间变成了0.05秒,这里需要注意在加索引的时候尤其是在线上系统特别繁忙或者它有很多慢 sql 的时候,注意加索引前观察数据库中正在运行的 sql, 如果此时数据库中有正在运行的慢 sql 那加索引的这个操作就会被慢 sql 所阻塞,进而导致这个表上后续的访问都会被阻塞住。
加了索引之后再去看这个表的慢 sql 执行计划,可以看到确实使用了 ind_mo 字段,是刚才加的索引,但是这里有一个异常,扫描行数还是70多万,刚才已经验证的这个字段它满足的行数是0行,但是为什么这个执行计划里 rows 还是70多万行,所以还需要再去分析一下为你什么这里出现问题。
接着往下分析为什么索引的过滤性这么差?有一个方法是通过 expiain 加 extended, extended 是可以把执行计划里内部的一些信息更近一步的打印出来,所以此时去看 expiain 加 extended 这个 sql, 再去看 show warnings, 可以看到 Warnings 它不能够使用索引,因为这个 type 和 collation 发生了转换,所以我们可以看到为什么这个执行计划它是70多万行的扫描数据,原因是由于这个字段类型或者是 collation, 校验数据的对比,这样一个问题导致了索引不能使用,所以去看一下这个表结构,是否是它的表结构出现了问题。
具体去看这个表结构,mo 字段的定义是 char,是一个字符串的数据类型,但是可以看到我们传入的数据,sql 里传入的是数字,所以这里出现问题了,sql 里传入的是数字,但表结构里传入的是字符串,那就会导致索引发生这个隐式转换。
二、隐式转换案例
知道原因就可以知道解决方法,保持 sql 里传入的值和字段定义的值保持一致都换成数字,看一下执行计划最终的 rows 变成了1行,执行时间也变成了0秒,所以这是最终通过加索引再到进一步定位这个字段它发生的隐式转换,通过这两步把 sql 从2秒的执行时间调整到0秒。
所以通过这个问题再去回顾一下案例,在数据库出现 CPU 100%的时候,要去看数据库中当前正在运行的 sql, 看它的 sql 出现了什么样的堆积状况,可以通过 super csdiste 或者查看iDste 控制台,都可以看到要优化的 sql, 再通过 explain 去看 sql 的执行计划,判断 sql 是否使用了索引或者说即使使用了索引但是它是否是最优的,所以可以看到刚刚案例它第一个没有索引,
第二个它没有发生隐式转换。再去进一步分析时,扩展一下发生隐式转换时在计划里可以看到有一类数据类型,最常见的是在表结构定义时定义成字符串,但是在 sql 或者在应用里传入时变成了数字,这样就会导致隐式转换,还有一类是在定义字段的 collation 区分大小写,比如姓名区分大小写,但另一个表的字段又没有大小写,这样就会导致隐式转换,所以这里要特别注意表结构应用上限的设计之初避免掉这些问题,防止数据量越大再去解决这个问题,
所以在设计开发阶段,第一要避免数据库字段定义和应用传入的参数而出现不一致的情况。Mysql 数据不像 orcal 数据库一样它不支持函数索引,所以有时从 orcal 里牵出来的应用可能会出现在 orcal 里运行的很好的 sql, 但在 Mysql 里运行的就比较差,比如在时间字段上加一个 date 函数,在 orcal 里是可以创建一个 date 的函数索引,但在 mysql 里就不支持了,所以要把 date 去掉;
第四个是在应用上线前去把 sql 审核,sql 是非常重要的代码,必须经过严格的审核,把每一道 sql 的访问次数,是否创建索引等一些审核才能进行上线。