diff --git a/planner/core/plan_cost_ver2.go b/planner/core/plan_cost_ver2.go index b53d53c845c45..c6fe4dd0b341c 100644 --- a/planner/core/plan_cost_ver2.go +++ b/planner/core/plan_cost_ver2.go @@ -559,6 +559,7 @@ func (p *PhysicalIndexJoin) getIndexJoinCostVer2(taskType property.TaskType, opt probeConcurrency := float64(p.ctx.GetSessionVars().IndexLookupJoinConcurrency()) cpuFactor := getTaskCPUFactorVer2(p, taskType) memFactor := getTaskMemFactorVer2(p, taskType) + requestFactor := getTaskRequestFactorVer2(p, taskType) buildFilterCost := filterCostVer2(option, buildRows, buildFilters, cpuFactor) buildChildCost, err := build.getPlanCostVer2(taskType, option) @@ -595,7 +596,18 @@ func (p *PhysicalIndexJoin) getIndexJoinCostVer2(taskType property.TaskType, opt batchRatio := 6.0 probeCost := divCostVer2(mulCostVer2(probeChildCost, buildRows), batchRatio) - p.planCostVer2 = sumCostVer2(startCost, buildChildCost, buildFilterCost, buildTaskCost, divCostVer2(sumCostVer2(probeCost, probeFilterCost, hashTableCost), probeConcurrency)) + // Double Read Cost + doubleReadCost := newZeroCostVer2(traceCost(option)) + if p.ctx.GetSessionVars().IndexJoinDoubleReadPenaltyCostRate > 0 && + buildRows > 10000 { // ignore double-read costs for small IndexJoin + batchSize := float64(p.ctx.GetSessionVars().IndexJoinBatchSize) + taskPerBatch := 1024.0 // TODO: remove this magic number + doubleReadTasks := buildRows / batchSize * taskPerBatch + doubleReadCost = doubleReadCostVer2(option, doubleReadTasks, requestFactor) + doubleReadCost = mulCostVer2(doubleReadCost, p.ctx.GetSessionVars().IndexJoinDoubleReadPenaltyCostRate) + } + + p.planCostVer2 = sumCostVer2(startCost, buildChildCost, buildFilterCost, buildTaskCost, divCostVer2(sumCostVer2(doubleReadCost, probeCost, probeFilterCost, hashTableCost), probeConcurrency)) p.planCostInit = true return p.planCostVer2, nil } diff --git a/sessionctx/variable/session.go b/sessionctx/variable/session.go index 47d7eef068ed2..1c1fa91edbc6a 100644 --- a/sessionctx/variable/session.go +++ b/sessionctx/variable/session.go @@ -1207,6 +1207,9 @@ type SessionVars struct { EnableNewCostInterface bool // CostModelVersion is a internal switch to indicates the Cost Model Version. CostModelVersion int + // IndexJoinDoubleReadPenaltyCostRate indicates whether to add some penalty cost to IndexJoin and how much of it. + IndexJoinDoubleReadPenaltyCostRate float64 + // BatchPendingTiFlashCount shows the threshold of pending TiFlash tables when batch adding. BatchPendingTiFlashCount int // RcWriteCheckTS indicates whether some special write statements don't get latest tso from PD at RC diff --git a/sessionctx/variable/sysvar.go b/sessionctx/variable/sysvar.go index 25e868b7c21f2..a608c34c2c720 100644 --- a/sessionctx/variable/sysvar.go +++ b/sessionctx/variable/sysvar.go @@ -1981,6 +1981,12 @@ var defaultSysVars = []*SysVar{ return nil }, }, + {Scope: ScopeGlobal | ScopeSession, Name: TiDBIndexJoinDoubleReadPenaltyCostRate, Value: strconv.Itoa(1), Hidden: false, Type: TypeFloat, MinValue: 0, MaxValue: math.MaxUint64, + SetSession: func(vars *SessionVars, s string) error { + vars.IndexJoinDoubleReadPenaltyCostRate = tidbOptFloat64(s, 0) + return nil + }, + }, {Scope: ScopeGlobal | ScopeSession, Name: TiDBRCWriteCheckTs, Type: TypeBool, Value: BoolToOnOff(DefTiDBRcWriteCheckTs), SetSession: func(s *SessionVars, val string) error { s.RcWriteCheckTS = TiDBOptOn(val) return nil diff --git a/sessionctx/variable/tidb_vars.go b/sessionctx/variable/tidb_vars.go index a5758ced155ac..05748a73b06b5 100644 --- a/sessionctx/variable/tidb_vars.go +++ b/sessionctx/variable/tidb_vars.go @@ -700,6 +700,12 @@ const ( // TiDBCostModelVersion is a internal switch to indicates the cost model version. TiDBCostModelVersion = "tidb_cost_model_version" + // TiDBIndexJoinDoubleReadPenaltyCostRate indicates whether to add some penalty cost to IndexJoin and how much of it. + // IndexJoin can cause plenty of extra double read tasks, which consume lots of resources and take a long time. + // Since the number of double read tasks is hard to estimated accurately, we leave this variable to let us can adjust this + // part of cost manually. + TiDBIndexJoinDoubleReadPenaltyCostRate = "tidb_index_join_double_read_penalty_cost_rate" + // TiDBBatchPendingTiFlashCount indicates the maximum count of non-available TiFlash tables. TiDBBatchPendingTiFlashCount = "tidb_batch_pending_tiflash_count"