对 Indexlookup 的理解误区
在了解 IndexLookUp 执行过程前,先介绍下 mysql 索引扫描的执行作为对比(此处借用网络图),一条 SQL 执行时在存储引擎侧首先通读取索引中符合条件记录的主键(可能涉及 ICP、组合索引部分列等),然后根据主键去表中读取记录,之后将记录返回给 server 层,再由 server 层根据其他条件进行过滤后返回客户端。上述过程中索引扫描和回表操作都是在存储引擎侧完成。
![](file:///C:\Users\unicom\AppData\Local\Temp\msohtmlclip1\01\clip_image002.png)
IndexLookUp 算子是 tidb 通过扫描索引然后通过 rowid 回表的扫描过程,包含 2 个子算子 IndexScan 和 TableRowidscan,其中 IndexLookUp 算子的执行计划 task 列为 root 表示该算子在 tidb server 侧执行,2 个子算子的 task 列为 cop[tikv]表示 coprocessor task 下推到 tikv 执行,在执行计划中算子的执行顺序是先执行子算子然后返回数据给父算子,对于并列深度的子算子上面的先执行(build 端),然后用返回结果执行另一个算子(probe 端),但执行过程中并不严格要求所有算子都是执行完后才返回结果或者子算子全部完成后才向父算子返回结果。
![](file:///C:\Users\unicom\AppData\Local\Temp\msohtmlclip1\01\clip_image003.png)
官方文档中对 IndexLookUp 介绍如下:
先汇总 Build 端 TiKV 扫描上来的 RowID,再去 Probe 端上根据这些 RowID 精确地读取 TiKV 上的数据。Build 端是 IndexFullScan 或 IndexRangeScan 类型的算子,Probe 端是 TableRowIDScan 类型的算子。
一直以来由于对子算子(IndexScan 和 TableRowidscan)在 tikv 中执行想当然的认为索引扫描和 mysql 一样都是在存储引擎一侧完成,导致对 IndexLookUp 执行过程有错误的认识:在 tikv 端通过索引扫描获得 rowid 后在 tikv 内使用 rowid 回表查询,最后将结果返回给 tidb server 再次过滤或返回客户端。
实际上 IndexLookUp 执行过程如下:
(1) tidb server 根据 SQL 条件构造 index scan 的 cop task,在 tikv 侧扫描索引获得符合条件的 rowid.
(2) 扫描出来的 rowid 返回给 tidb server 进行汇总, tidb server 根据 rowid 构造 Key 和 cop task 执行回表操作
(3) tikv 使用 TableRowIDScan 回表扫描数据,然后将数据返回 tidb,执行计划中的时间包括网络时间。
相比于 mysql 直接在引擎内直接回表查询,tidb IndexLookUp 执行过程中要多 2 次网络交互,不可避免的会造成延迟,因此网络性能、tidb server 的处理能力对 IndexLookUp 性能有一定影响。
Tidb 有相关参数控制 indexlookup 的性能:
tidb_index_lookup_size:使用 rowid 扫描表时一个批次处理的数量
tidb_index_lookup_concurrency:使用 rowid 扫描表时的并发数。
测试 1: tidb_index_lookup_concurrency 影响
Concurrency 由 5 降为 1 后,执行时间由 6.15 秒增长为 26.57 秒
![](file:///C:\Users\unicom\AppData\Local\Temp\msohtmlclip1\01\clip_image005.jpg)
测试 2:tidb_index_lookup_size 影响
相比较提高并发数 tidb_index_lookup_concurrency 提升效果不是很明显
![](file:///C:\Users\unicom\AppData\Local\Temp\msohtmlclip1\01\clip_image007.jpg)
原文作者:@h5n1 发表于:2022/4/8
评论