本来看代码有点找不到了,经过 @h5n1 提醒:
explain format=‘cost_trace’ 这个看执行计划有cost计算公式
这个问题有了一些进展,权当抛砖引玉好了。
我找了一张大表,大致有3.5亿数据,analyze之后,根据时间片查询的结果如下:
index scan:
table scan:
具体的公式格式化结果后,index scan:
table scan:
这个看着还是有点乱。让我们把重点突出一下。
在index scan的这个公式里面,起决定性作用的是:
(doubleRead(tasks(36.685874119007586)*tidb_request_factor(6e+06)))
)/tidb_executor_concurrency=5.00
因为tidb_request_factor的代价设置非常高,一次就是6000000.
https://github.com/pingcap/tidb/blob/master/pkg/planner/core/plan_cost_ver2.go#L940
所以当index scan发生的时候,其cost基本可以简化为上面这个式子,其他的影响权重非常小。
task的数量的公式为
https://github.com/pingcap/tidb/blob/master/pkg/planner/core/plan_cost_ver2.go#L275
batchSize := float64(p.SCtx().GetSessionVars().IndexLookupSize)
taskPerBatch := 32.0 // TODO: remove this magic number
doubleReadTasks := doubleReadRows / batchSize * taskPerBatch
IndexLookupSize 可以通过show variables like 'tidb_index_lookup_size'
查看,我这边设置为20000。
所以tasks=预估读取行数(22928)/tidb_index_lookup_size(20000)*taskPerBatch(32)=36.68左右。
因此,现在如果我们希望index scan的代价变低,比较有效的做法有两个,提高tidb_index_lookup_size和tidb_executor_concurrency的值。
同理,我们看看table scan的代价计算方式,主要的部分是:
(
(cpu(3.5718153e+07(总记录数)*filters(1)*tikv_cpu_factor(49.9))) +
(scan(3.5718153e+07(总记录数)*logrowsize(312)*tikv_scan_factor(40.7)))
)/(tidb_distsql_scan_concurrency)15.00
net的部分cost影响权重很小,可以直接忽略,cost值主要的影响因素在于全表记录大小和tidb_distsql_scan_concurrency的值的大小。
全表记录大小是没有办法调整的,为了降低table scan 的cost,那就只剩下调大tidb_distsql_scan_concurrency的值,这一个办法。
回到标题的问题。这个比例是不太好算的,但可以大致推算,当
(index scan的预估记录数/tidb_index_lookup_size*32*tidb_request_factor)/tidb_executor_concurrency小于
表的总记录数* logrowsize*tikv_scan_factor/tidb_distsql_scan_concurrency
大体就会选择index scan而非table scan。同时如果为了让优化器尽可能的选择index scan还可以通过2种参数设置来做到:
1,提高tidb_index_lookup_size的值
2,提高tidb_executor_concurrency的值