本测试源于问题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