作者:闫彬彬​

 

       本测试源于问题​​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亿的表。表结构如下:

MVCC导致limit 1执行慢测试_删除表

    表使用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

MVCC导致limit 1执行慢测试_sql_02

       使用sbtest2表,可以看到limit_10 task列为cop[tikv] 表示已下推到tikv。在完全没有对表做数据变更的情况下,执行时间1.85ms,仅执行1个cop_task,total_process_keys: 1,读取了read_byte: 29.2 KB

MVCC导致limit 1执行慢测试_sql_03

MVCC导致limit 1执行慢测试_删除表_04



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。

MVCC导致limit 1执行慢测试_sql_05

MVCC导致limit 1执行慢测试_删除表_06



4.3     删除全部数据

       测试使用sbtest3表,共执行497个cop_task, total_process_keys: 0, , total_keys: 412981739,读取19.7G。

MVCC导致limit 1执行慢测试_sql_07

 



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。

MVCC导致limit 1执行慢测试_删除表_08



4.5     删除表后段数据

        使用SQL:delete from sbtest6 where id>100533351;从第一个region中就找到了符合条件数据,因此仅执行1个cop_task,SQL执行8ms ,total_process_keys: 1, total_keys: 2。

MVCC导致limit 1执行慢测试_删除表_09

      

MVCC导致limit 1执行慢测试_删除表_10


MVCC导致limit 1执行慢测试_删除表_11



4.6     Truncate后测试

       使用sbtest2表,Truncate后仅1个region, 因此仅有1个cop_task

MVCC导致limit 1执行慢测试_数据_12



4.7    禁用limit下推tikv

       使用sbtest7表未做任何DML,禁用Limit下推后执行计划中少了一个cop[tikv]的limit算子, tikv侧数据扫描返回41万行,虽然执行计划中显示cop_task数量为1,但实际下发tidb_distsql_scan_concurrency参数个cop_task到tikv


MVCC导致limit 1执行慢测试_删除表_13

MVCC导致limit 1执行慢测试_sql_14


MVCC导致limit 1执行慢测试_删除表_15

调整tidb_distsql_scan_concurrency参数后的执行

MVCC导致limit 1执行慢测试_数据_16



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​