「MySQL」比较冷门的Index Merge
分享一个冷门的sql优化小技巧,主要看好多讲索引type都不带上这个。这里讲一下基本概念以及优化分享。
瞎聊
最近翻了翻八股,大部分聊explain的时候会忽略index merge,这个在我日常调优的时候经常会遇到,对于概念只能说有一个大概的了解,这次做一个总结归纳哈。
type类型介绍
- system:仅一行数据
- const:表中最多存在一个匹配行。常用于primary key或者唯一索引
- eq_ref:对于前面表的行组合,从该表中读取一行。
- ref :对于前面表的行组合,在此表的索引中可以匹配到多行。
- eq_or_null:和null类似,增加了null值的比较。
- index_merge:索引合并优化。使用了两个以上的索引,最后取交集或者并集
- unique_subquery:替代了eq_ref的一些子查询
unique_subquery
- index_subquery:类似unique_subquery,区别是唯一索引和非唯一索引的区别
- range: 索引范围查询,常见于使用 =, <>, >, >=, <, <=, IS NULL, <=>, BETWEEN, IN()或者like等运算符的查询中。
- index: 该联接类型与 ALL 相同,除了只有索引树被扫描。
- all: 全表扫描
整体的一个执行情况排序,一般来说,得保证查询至少达到range级别,最好能达到ref。
system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL
index merge介绍
官网链接:dev.mysql.com/doc/refman/…
索引合并访问方法检索具有多个 range扫描的行并将其结果合并为一个。此访问方法仅合并来自单个表的索引扫描,而不合并跨多个表的扫描。合并可以生成其基础扫描的并集、交集或交集的并集。
这个怎么说呢你看到英文应该就能猜的大概,mysql是会走多个索引的,然后将多个索引的结果进行合并
一般是三种类型:
-
Using intersect(...) :交集
key_part1 = const1 AND key_part2 = const2 ... AND key_partN = constN
-
Using union(...) : 并集
key_part1 = const1 OR key_part2 = const2 ... OR key_partN = constN
-
Using sort_union(...) :有序并集
SELECT * FROM tbl_name WHERE key_col1 < 10 OR key_col2 < 20;
这三个索引优化都是可以控制开关的可以通过SELECT @@optimizer_switch
进行查看。
大致原理介绍
参考文献:mysql.taobao.org/monthly/202…
由于实在看不懂c了,这里就偷懒截图了。大致流程我简述一下, 计算走每一个索引的代价,如果条件符合还会计算多个索引的代价计算。 然后从上面计算出一个最优的方式去执行。
原文:
- 计算 table scan cost
- 分配 PARAM 结构,初始化相关字段
- 对表上每个可用的 key,加入 param.key[] 数组,其中每个 key_part,对应一个 KEY_PART 对象(param.key_parts数组)
- 考虑是否能使用覆盖索引,首先调用find_shortest_key()函数,选择KEY::key_length最小的可用索引。接着计算索引扫描代价,比较是否比 table scan 代价更低。
- 考虑各种 range scan
- 调用get_mm_tree()函数,根据 ON/WHERE 携带的条件,构建 range tree。
- get_best_group_min_max() 尝试为 group by 构建 QUICK_GROUP_MIN_MAX_SELECT。
- get_best_skip_scan(),对于单表非 group by,尝试构建 skip scan
- get_key_scans_params()函数对 PARAM.key[] 中的 key,依次调用 check_quick_select,计算得到的行数 found_records 和代价 cost。选择更低代价的 range,创建 TRP_RANGE 结构。
- get_best_ror_intersect
- get_best_disjunct_quick,计算 index merge union 的代价
- make_quick(),找到最优的 best_trp 后,创建 QUICK_SELECT_I 对应的子类对象,设置给JOIN_TAB
简单实践
trace不重要,主要是想说明一下,索引合并是要计算代价的,计算代价也是需要成本的。这个说法是来源《高性能MySQL》
但是,一般来说,建立区分度高的联合索引肯定好。但是有2个点也是需要考虑,
- 索引也是需要空间的
- 你在这个表创立的初期,很难预测以后产品会将哪个作为重点,比如在下,压错过几次。所以我加索引相对而言比较保守,是先加单个字段的索引,后期根据慢查询统计建立适当的索引。
在此期间,会经常看到索引合并的例子,但是这个也是经常被忽视的一个点,特此整理一下
参考文档
转载自:https://juejin.cn/post/7396580330704486434