📫作者简介:小明java问道之路,专注于研究 Java/ Liunx内核/ C++及汇编/计算机底层原理/源码,就职于大型金融公司后端高级工程师,擅长交易领域的高安全/可用/并发/性能的架构设计与演进、系统优化与稳定性建设。
📫 热衷分享,喜欢原创~ 关注我会给你带来一些不一样的认知和成长。
🏆 InfoQ签约作者、CSDN专家博主/后端领域优质创作者/内容合伙人、阿里云专家/签约博主、51CTO专家 🏆
🔥如果此文还不错的话,还请👍关注、点赞、收藏三连支持👍一下博主~
高性能高可用设计实战-索引篇,结合上一篇文章 《深入理解MySQL索引原理》,本篇通过组合(联合、覆盖)索引是如何在生产环境中提升查询效率的,引出MySQL最左匹配原则和MySQL选择索引的方式。并给出了企业中MySQL创建索引设计规范和MySQL索引从设计到生产调优的过程。
组合索引是由多个列组成的B+树索引。这与我们前面介绍的B+树索引的原理完全相同,只是它以前对一列进行排序,现在对多列进行排序。组合索引可以是主键索引或辅助索引,没有限制。
组合索引本质上是B+树索引。它只从一个键值更改为多个键值。
当选择复合索引时,MySQL 是计算 key_len,以了解有效索引长度对于索引优化。
key_len 计算规则,key_Len 表示用于获取结果集的选定索引的长度[字节数],不包括order by,也就是说,如果order by也使用索引,则key_Len不计算在内。(key_Len在第三节MySQL选择索引的原理中详解)
key_len计算规则从两个方面考虑:一个是索引字段的数据类型,另一个是表和字段使用的字符集。
假设我们设置下面sql的索引,需要注意 (`LOG_ID`, `SUB_ODR_ID`) 和 (`SUB_ODR_ID`, `LOG_ID`) 这样的组合索引,其排序结果是完全不一样的。
INDEX `UNQ_LOG_SUBODR_ID`(`LOG_ID`, `SUB_ODR_ID`) USING BTREE,
对组合索引 (`LOG_ID`, `SUB_ODR_ID`) 来说,因为其对列 log_id、sub_odr_id 做了排序,所以它可以对下面两个查询进行优化,但是下面的 SQL 无法使用组合索引 (`LOG_ID`, `SUB_ODR_ID`) ,因为 (`LOG_ID`, `SUB_ODR_ID`) 排序并不能推出 (`SUB_ODR_ID`, `LOG_ID`) 排序,如下代码所示:
- -- 可以执行联合索引
- SELECT * FROM table WHERE LOG_ID = ?
- SELECT * FROM table WHERE LOG_ID = ? AND SUB_ODR_ID = ?
-
- -- 不可执行索引
- SELECT * FROM table WHERE SUB_ODR_ID = ?
-
- -- 可以执行联合索引
- SELECT * FROM table WHERE LOG_ID = ? ORDER BY SUB_ODR_ID DESC
-
- -- 不可执行索引
- SELECT * FROM table WHERE SUB_ODR_ID = ? ORDER BY LOG_ID DESC
当我们ORDER BY create_date DESC ,如果时间字段没有索引,会首先进行 Index lookup 索引查询,然后进行 Sort 排序,最终得到结果。但是where 后查询的列有创建索引,所以上述SQL语句的执行不会特别慢。
然而,在大规模并发业务访问下,每次SQL执行都需要排序,这将对业务性能产生非常显著的影响,例如较高的CPU负载和较低的QPS。
要解决这个问题,最好的方法是:当检索结果时,字段 create_date 排序,因此不需要额外的排序。
建议,我们在表上创建新的组合索引 idx_***_create_date,对字段(*** ,create_date)
上一篇文章 《深入理解MySQL索引原理》,已经讲了回表的概念,即SQL需要通过二级索引查询获得主键值,然后根据主键值搜索主键索引,最后找到完整的数据。
但是,由于组合索引的叶节点包含索引键值和主键值,因此如果查询的字段位于组合索引的叶子节点中,则可以直接返回结果而不返回表。这种通过组合索引避免返回表的优化技术也称为覆盖索引。
回表次数是根据查询条件判断的
如果查询单条数据,没有覆盖索引的话,只回表一次。
如果查询数据量大的话,比如查询200条数据,就需要回表200次。
若表的记录数越多,需要回表的次数也就越多,通过索引覆盖技术性能的提升也就越明显。
通过 key_len 计算可以帮助我们理解索引的最左前缀匹配原理。
最左边的前缀匹配原则意味着,当使用B+树联合索引进行数据检索时,MySQL 优化器将读取过滤条件(谓词),并根据联合索引字段的创建顺序将其自左向右匹配,直到遇到范围查询或非等效查询后停止匹配。将不使用此字段后面的索引列。
这时计算 key_len 可以分析联邦索引实际使用了哪些索引列。
B+ 树可以利用索引的“最左前缀”,来定位记录。
基于上面对最左前缀索引的说明,在建立联合索引的时候,如何安排索引内的字段顺序?
评估标准是,索引的复用能力。
因为可以支持最左前缀,所以当已经有了 (a,b) 这个联合索引后,一般就不需要单独在 a 上建立索引了。因此,第一原则是,如果通过调整顺序,可以少维护一个索引,那么这个顺序往往就是需要优先考虑采用的。
首先,如何在创建索引后确认SQL语句是否被索引?
创建索引后,您可以通过查看SQL语句的执行计划(explain)来了解SQL语句是否被索引。执行计划侧重于与索引相关的关键项,包括 type、possible_keys、key、key_len、ref、Extra 等。
possible_Keys表示可能用于查询的索引
key表示实际使用的索引
key_len表示使用的索引字段的长度。
Extra 显示 use index 时就表示该索引是覆盖索引,表示该索引是覆盖索引,通常,性能排序的结果是usd index>use where>use filsort
当选择复合索引时,MySQL 是计算 key_len,以了解有效索引长度对于索引优化。
key_len 计算规则,key_Len 表示用于获取结果集的选定索引的长度[字节数],不包括order by,也就是说,如果order by也使用索引,则key_Len不计算在内。
key_len计算规则从两个方面考虑:一个是索引字段的数据类型,另一个是表和字段使用的字符集。
1、每个公司的命名标准都是统一的,首先应该遵守公司DBA规定。
2、表必须具有主键。建议使用 UNSIGNED 自增列作为主键。
3、考虑到索引维护的成本,一般情况下单个索引中的字段数不超出5个。
当没有为表设置主键时,InnoDB将默认设置隐藏的主键列。表不方便定位数据,也会增加MySQL的运维成本(例如主从复制效率严重受损)。
4、唯一索引由三个或更少的字段组成,不建议设置为主键。
5、由于索引维护需要成本,建议删除冗余索引和重复索引。
添加新索引时,应优先考虑基于现有索引进行重建,如(a,b,c)和(a),这些索引是冗余索引,占用磁盘空间,可以删除。
6、查询关联表时,JOIN 列的数据类型必须相同,并且必须创建索引。
7、不在低区分度的列增加索引,选择具有较大辨别力(选择率)的列以构建索引。在复合索引中,具有高分辨力(选择率)的字段被放在第一位。
8、合理使用覆盖索引以减少IO并避免排序。
因为如果SQL需要查询未包含在辅助索引中的数据列,需要首先通过辅助索引找到主键值,然后通过主键查询返回表以查找其他数据列(即返回表查询)。需要查询两次,覆盖索引可以直接从索引中获取查询所需的所有数据,并避免在后表中进行二次查找,节省了IO,效率更高。
1、查找由不适当或缺少索引导致的慢sql
通常,在创建数据库或表时,需要将与业务操作相关的SQL提交给DBA审批。同时可以进行慢查询日志分析,抓取慢运行的SQL进行分析,慢查询监控系统用于慢查询收集和分析。在慢查询分析中,执行最差的参数输入,分析了SQL语句谓词的过滤因子、基数、选择率和SQL回表情况。
2、设计索引
索引设计的目标是使查询语句运行得足够快,同时也使表和索引维护足够快。例如,使用与业务无关的自增加字段作为主键,以降低索引维护(如页面拆分和页面合并)的成本,加快性能。
3、创建索引策略
优先为搜索列、排序列和分组列创建索引,必要时添加查询列以创建覆盖索引;
索引应为较小的数据类型(整数类型优于字符类型),字符串可考虑前缀索引;
不要创建太多索引,并根据现有索引调整顺序。
4、调优索引
分析执行计划;分析MySQL优化器,这部分将在 《MySQL优化器工作原理》详细讲解。
高性能高可用设计实战-索引篇,结合上一篇文章 《深入理解MySQL索引原理》,本篇通过组合(联合、覆盖)索引是如何在生产环境中提升查询效率的,引出MySQL最左匹配原则和MySQL选择索引的方式。并给出了企业中MySQL创建索引设计规范和MySQL索引从设计到生产调优的过程。