对Indexlookup的理解误区
在了解IndexLookUp执行过程前,先介绍下mysql索引扫描的执行作为对比(此处借用网络图),一条SQL执行时在存储引擎侧首先通读取索引中符合条件记录的主键(可能涉及ICP、组合索引部分列等),然后根据主键去表中读取记录,之后将记录返回给server层,再由server层根据其他条件进行过滤后返回客户端。上述过程中索引扫描和回表操作都是在存储引擎侧完成。
IndexLookUp算子是tidb通过扫描索引然后通过rowid回表的扫描过程,包含2个子算子IndexScan和TableRowidscan,其中IndexLookUp算子的执行计划task列为root表示该算子在tidb server侧执行,2个子算子的task列为cop[tikv]表示coprocessor task下推到tikv执行,在执行计划中算子的执行顺序是先执行子算子然后返回数据给父算子,对于并列深度的子算子上面的先执行(build端),然后用返回结果执行另一个算子(probe端),但执行过程中并不严格要求所有算子都是执行完后才返回结果或者子算子全部完成后才向父算子返回结果。
官方文档中对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秒
测试2:tidb_index_lookup_size影响
相比较提高并发数tidb_index_lookup_concurrency提升效果不是很明显