MVCC 导致 limit 1 执行慢测试
作者: h5n1 原文来源:https://tidb.net/blog/9ddaa53a
本测试源于问题https://asktug.com/t/topic/664215/20。
Tidb 执行 SQL 时根据条件构建 key range,然后根据 key range 去匹配相应的 region,针对每个 region 生成一个 coprocessor task 下发到 tikv 去执行。由于 tidb mvcc 实现原理在 DML 后保留历史版本,会导致在扫描时会读取历史版本,通过 explain analyze 的 total_process_keys 和 total_keys 的差可以判断,如果 2 者差距较大说明有较多的历史版本,可能是 GC 未完成处理或 GC 处理遇到问题。
对于 limit 使用下推到 tikv 的优化,使每个 coprocessor task 获得达到满足 Limit 条件的数据后即停止向 tidb server 返回数据,从而避免从 tikv 扫描大量数据后返回 tidb 在由 tidb 进行 limit 过滤,从而提升 SQL 性能。
对于全表扫描会每次下发 tidb_distsql_scan_concurrency 个 cop task 直到完成所有数据扫描,tidb 对简单的 tablescan+limit 方式做了优化,如果 limit 数量未超过 chunksize(1024),则可以根据 limit 动态调整下发 cop task 数量为 1,导致会按照 key range 顺序一个个的扫描 region:https://github.com/pingcap/tidb/issues/18791
1 测试版本
tidb v5.3.1 (arm 平台,4 tidb+6tikv+3PD)
2 测试环境
使用 sysbench 初始化 10 张 2 亿的表。表结构如下:
表使用 bigint 列作为主键,会以 id 列作为 rowid,region key 会按 id 顺序递增。
3 测试方式
每次执行相关 select 前,都 reload tikv 保证都进行物理读取, 以便对执行时间做对比。
4 测试过程
4.1 测试对比基准
分别对 2 个表进行全表扫描和 limit 限制观察正常情况下整个全表扫描和正常 limit 执行时的资源情况。
对 sbtest1 进行全表扫描,执行时间 3m0.2s,共执行 496 个 cop_task,total_process_keys: 200000000, total_keys: 287870027,读取 read_byte: 18.5 GB
使用 sbtest2 表, 可以看到 limit_10 task 列为 cop[tikv] 表示已下推到 tikv。在完全没有对表做数据变更的情况下,执行时间 1.85ms,仅执行 1 个 cop_task,total_process_keys: 1,读取了 read_byte: 29.2 KB
4.2 delete 中间数据
使用 delete from sbtest4 where id>6000 and id<20099300; 删除表中间部分数据,仅保留首、尾小部分数据。
SQL 执行 0.1 秒,从第一个 region 中就找到了符合条件数据,仅执行 1 个 cop_task,total_process_keys: 1, total_keys: 2。
4.3 删除全部数据
测试使用 sbtest3 表,共执行 497 个 cop_task, total_process_keys: 0, , total_keys: 412981739, 读取 19.7G。
4.4 删除表前段数据
使用 SQL:delete from sbtest5 where id<100533351; 由于删除表中前一半数据导致按 region key 扫描数据时跳过大量历史版本,SQL 执行 2m20.1s , 共 257 个 cop_task,total_process_keys: 1, total_keys: 199986568, total_keys: 2,读取 10.4G。
4.5 删除表后段数据
使用 SQL:delete from sbtest6 where id>100533351; 从第一个 region 中就找到了符合条件数据,因此仅执行 1 个 cop_task,SQL 执行 8ms ,total_process_keys: 1, total_keys: 2。
4.6 Truncate 后测试
使用 sbtest2 表,Truncate 后仅 1 个 region, 因此仅有 1 个 cop_task
4.7 禁用 limit 下推 tikv
使用 sbtest7 表未做任何 DML,禁用 Limit 下推后执行计划中少了一个 cop[tikv]的 limit 算子, tikv 侧数据扫描返回 41 万行,虽然执行计划中显示 cop_task 数量为 1, 但实际下发 tidb_distsql_scan_concurrency 参数个 cop_task 到 tikv
调整 tidb_distsql_scan_concurrency 参数后的执行
5 测试总结
(1) DML 后产生的 MVCC 版对 limit 执行性能会产生影响,根据数据分布情况不同,limit 的范围不同,影响大小不一样。
(2) Truncate 表后使用 drop+create 建立新表,因此不影响 limit,对于全表删除应使用 truncate 方式,也有利于 GC 回收
(3) Limit 不下推到 tikv 会导致扫描返回更多的结果,降低 SQL 性能,增加系统负载。
6 遗留问题
(1) 禁用 Limit 下推后,TableReader 算子基于何种算法返回 32 行?
(2) 禁用 Limit 下推后,执行计划中虽然显示为 1 个 cop task 却下发了 tidb_distsql_scan_concurrency 个 cop task?
https://asktug.com/t/topic/664580
版权声明: 本文为 InfoQ 作者【TiDB 社区干货传送门】的原创文章。
原文链接:【http://xie.infoq.cn/article/b5243eabc9bec357cb411dd30】。文章转载请联系作者。
评论