1 Multi-Range Read Optimization 多范围索引扫描读优化
在次级索引上使用范围扫描(range scan)读取行时,当表很大且没有存储再存储引擎的缓存中时,就可能会导致对基础表的的许多磁盘的随机访问。 Mysql 试图通过 Multi-Range Read (MRR) 优化来减少范围扫描时的随机磁盘访问
步骤:
- 首先只扫描索引。并收集相关行的键(keys for the relevent rows)
- 对这些键进行排序,使用键排序的顺序访基表检索数据行
优化的动机就是减少随机磁盘访问,实现对基表的更多的顺序扫描
优点:
- MRR 基于索引对,使得数据行的访问是顺序执行的,而不是随机的。Server 获取满足查询条件的索引对 (index tuple),根据数据行键(row ID)进行排序,使用排序的结果顺序访问完整的数据行。使得数据访问更高效,较低磁盘IO消耗
- MRR 允许批量处理对键访问的请求,这些请求用于需要通过索引对访问数据行的操作,比如范围索引扫描和使用索引连接属性的等值连接。MRR 迭代索引范围序列,获得满足条件的索引对。将这些结果进行聚合,访问对应的数据行。并且没有获取所有的索引对后才进行数据行的读取
MRR 不支持虚拟列上的次级索引.
哪些场景下可以利用 MRR 优化:
场景 A
InnoDB, MyISAM 表的索引范围扫描或者等值连接操作 (equi-join operations)
- 部分索引对被存储 (accumulated) 在缓存区中
- 缓冲区中的索引对通过数据行ID进行排序
- 根据排序的索引对的顺序访问数据行
场景 B
MRR 可以用于 NDB 表,用于多范围索引扫描,或者用于通过属性执行等值连接。
- 部分范围(可能是单键范围)缓存在查询提交的中心节点上的缓冲区中 (A portion of ranges, possibly single-key ranges, is accumulated in a buffer on the central node
where the query is submitted) - 这些范围查询被发送到执行节点来访问数据
- 数据行被打包发送回中心节点
- 将接收到的包 (带有数据行) 存放在缓冲区中
- 从缓冲区中返回结果行
- 部分范围(可能是单键范围)缓存在查询提交的中心节点上的缓冲区中 (A portion of ranges, possibly single-key ranges, is accumulated in a buffer on the central node
如何判断当前查询使用了 MRR 优化
执行计划的 Extra
列为 Using MRR
InnoDB, MyISAM 如果需要进行全表扫描时, 不会使用 MRR 优化. 例如: 如果仅仅基于索引对中的信息就可以获取查询结构, 即 覆盖索引, MRR 不会提供任何优化.
如何开启 MRR
optimizer_switch
系统变量提供了使用 MRR 优化的开关, mrr
标志表示 MRR 是否已开启 (on), 默认开启, mrr_cost_based
标志表示控制查询优化器是否尝试基于性能的选择, 使用或不适用 MRR, 默认开启 (on). 如果希望尽可能的使用 MRR , 将其置为 off
1 | SELECT @@optimizer_switch; |
read_rnd_buffer_size
系统变量表明可以为缓冲区分配的内存字节大小.
1 | -- 默认 262144B = 256KB |
示例
1 | show create table user_info; |
使用 in
的范围查询
1 | explain select * from user_info where uid in ('204-42-7061','735-14-7777','321-09-5846'); |
1 | -- in 的条件查询有 400 多个,后面的省略了 |
2 Block Nested-Loop and Batched Key Access Joins
MySQL 中可以使用 Batched Key Access (BKA)
连接算法来同时使用索引来访问被连接的表, 同样也使用一个连接缓冲区. BKA 算法支持内连接, 外连接, 以及半连接 (semijoin operations), 包括前内外连接. BKA 算法由于更高效的表扫描, 可以提升连接的性能. 同时, Block Nested-Loop(BNL)
连接算法也可以被扩展应用与的外连接等操作.
Join Buffer Management for Block Nested-Loop and Batched Key Access Algorithms
Block Nested-Loop Algorithm for Outer Joins and Semijoins
Batched Key Access Joins
Batched Key Access (BKA) 连接算法用于连接表, 当第二个连接操作数( the second join operand - 被驱动表) 表生成的行可以使用索引访问时, 可以使用应用 BKA 算法.
- 和 BNL 算法一样, BKA 连接算法会使用一个连接缓冲区 (join buffer) 来缓存连接操作中驱动表行的感兴趣的列.
- 然后 BKA 算法会构建缓冲区中所有行的键 (keys 索引) 来访问被连接的表
- 以批量的方式将这些 keys 提交给数据库引擎进行索引查询
- 提交的 keys 通过 MRR 接口进行处理, 以优化的方式, 使用索引进行查询
- 获取由这些 keys 匹配的行, 返回给 BKA 连接算法, 每一个匹配的行同时带了一个指向连接缓冲区中行的引用
使用 BKA 时, join_buffer_size
定义的时每次发给存储引擎的批量 keys 的大小
开启 BKA
optimizer_switch
系统变量的 batched_key_access
需要置为 on
, BKA 同时使用 MRR, mrr
也需要置为 on
. 并且由于目前对于使用 MRR 所进行的优化效果过于悲观, 需要将 mrr_cost_based
置为 off
1 | SET optimizer_switch='mrr=on,mrr_cost_based=off,batched_key_access=on'; |
如何判断当前连接查询使用了 BKA 算法
执行计划的 Extra
列为 Using join buffer (Batched Key Access)
, 同时, 连接类型 type
是 ref
或者 eq_ref
MRR 的执行由由两种场景
- 传统的基于磁盘的存储引擎, 如 InnoDB 和 MyISAM, 对于这些引擎. 来自连接缓冲区的所有行的键一次性提交给 MRR. 特定于引擎的 MRR 函数对提交的键进行索引查找, 从他们中获取行 id (或主键), 然后获取所有行 id 对应的行数据. 返回的每一行都带了一个关联引用 (用于访问连接缓冲区中的行). MRR 读取行时使用了行 id的顺序进行读取 (顺序访问而不是随机访问).
- 远程存储引擎, 如 NDB, MySQL Server (SQL 节点)将连接缓冲区的部分行的键及其关联引用发送到 NDB 集群的数据节点. SQL 节点收到数据节点返回的包(一个或多个), 其中包含了匹配的行以及相应的关联引用. BKA 算法利用这些行并构建新的连接行. 然后想数据节点发送行的键, 知道所有的连接操作匹配已经完成.
示例
开启 BKA
1 | SET optimizer_switch='mrr=on,mrr_cost_based=off,batched_key_access=on'; |
1 | show create table user_score; |
连接查询, 并且被驱动表的连接类型是 ref
, eq_ref
1 | explain select * from user_score a left join user_info b on a.uid = b.uid; |
关掉 BKA, 还原默认设置
1 | SET optimizer_switch='mrr=on,mrr_cost_based=on,batched_key_access=off'; |
*被驱动表的连接类型不是 ref
, eq_ref
*
1 | explain select * from user_info a left join user_score b on a.uid = b.uid; |