Skip to content

Commit

Permalink
planner: refactor to put all plan-cacheability-check functions togeth…
Browse files Browse the repository at this point in the history
…er (#40625)

ref #36598
  • Loading branch information
qw4990 committed Jan 17, 2023
1 parent 17df596 commit bdc6f4b
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 27 deletions.
11 changes: 0 additions & 11 deletions planner/core/optimizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,6 @@ func postOptimize(ctx context.Context, sctx sessionctx.Context, plan PhysicalPla
plan = eliminateUnionScanAndLock(sctx, plan)
plan = enableParallelApply(sctx, plan)
handleFineGrainedShuffle(ctx, sctx, plan)
checkPlanCacheable(sctx, plan)
propagateProbeParents(plan, nil)
countStarRewrite(plan)
return plan, nil
Expand Down Expand Up @@ -966,16 +965,6 @@ func setupFineGrainedShuffleInternal(ctx context.Context, sctx sessionctx.Contex
}
}

// checkPlanCacheable used to check whether a plan can be cached. Plans that
// meet the following characteristics cannot be cached:
// 1. Use the TiFlash engine.
// Todo: make more careful check here.
func checkPlanCacheable(sctx sessionctx.Context, plan PhysicalPlan) {
if sctx.GetSessionVars().StmtCtx.UseCache && useTiFlash(plan) {
sctx.GetSessionVars().StmtCtx.SetSkipPlanCache(errors.Errorf("skip plan-cache: TiFlash plan is un-cacheable"))
}
}

// propagateProbeParents doesn't affect the execution plan, it only sets the probeParents field of a PhysicalPlan.
// It's for handling the inconsistency between row count in the statsInfo and the recorded actual row count. Please
// see comments in PhysicalPlan for details.
Expand Down
63 changes: 47 additions & 16 deletions planner/core/plan_cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ func GetPlanFromSessionPlanCache(ctx context.Context, sctx sessionctx.Context,
}
}

paramNum, paramTypes := parseParamTypes(sctx, params)
paramTypes := parseParamTypes(sctx, params)

if stmtCtx.UseCache && stmtAst.CachedPlan != nil { // for point query plan
if plan, names, ok, err := getCachedPointPlan(stmtAst, sessVars, stmtCtx); ok {
Expand All @@ -176,12 +176,11 @@ func GetPlanFromSessionPlanCache(ctx context.Context, sctx sessionctx.Context,
}
}

return generateNewPlan(ctx, sctx, isNonPrepared, is, stmt, cacheKey, latestSchemaVersion, paramNum, paramTypes, bindSQL, limitCountAndOffset)
return generateNewPlan(ctx, sctx, isNonPrepared, is, stmt, cacheKey, latestSchemaVersion, paramTypes, bindSQL, limitCountAndOffset)
}

// parseParamTypes get parameters' types in PREPARE statement
func parseParamTypes(sctx sessionctx.Context, params []expression.Expression) (paramNum int, paramTypes []*types.FieldType) {
paramNum = len(params)
func parseParamTypes(sctx sessionctx.Context, params []expression.Expression) (paramTypes []*types.FieldType) {
for _, param := range params {
if c, ok := param.(*expression.Constant); ok { // from binary protocol
paramTypes = append(paramTypes, c.GetType())
Expand Down Expand Up @@ -267,8 +266,9 @@ func getCachedPlan(sctx sessionctx.Context, isNonPrepared bool, cacheKey kvcache

// generateNewPlan call the optimizer to generate a new plan for current statement
// and try to add it to cache
func generateNewPlan(ctx context.Context, sctx sessionctx.Context, isNonPrepared bool, is infoschema.InfoSchema, stmt *PlanCacheStmt, cacheKey kvcache.Key, latestSchemaVersion int64, paramNum int,
paramTypes []*types.FieldType, bindSQL string, limitParams []uint64) (Plan, []*types.FieldName, error) {
func generateNewPlan(ctx context.Context, sctx sessionctx.Context, isNonPrepared bool, is infoschema.InfoSchema,
stmt *PlanCacheStmt, cacheKey kvcache.Key, latestSchemaVersion int64, paramTypes []*types.FieldType,
bindSQL string, limitParams []uint64) (Plan, []*types.FieldName, error) {
stmtAst := stmt.PreparedAst
sessVars := sctx.GetSessionVars()
stmtCtx := sessVars.StmtCtx
Expand All @@ -285,10 +285,10 @@ func generateNewPlan(ctx context.Context, sctx sessionctx.Context, isNonPrepared
return nil, nil, err
}

// We only cache the tableDual plan when the number of parameters are zero.
if containTableDual(p) && paramNum > 0 {
stmtCtx.SetSkipPlanCache(errors.New("skip plan-cache: get a TableDual plan"))
}
// check whether this plan is cacheable.
checkPlanCacheability(sctx, p, len(paramTypes))

// put this plan into the plan cache.
if stmtCtx.UseCache {
// rebuild key to exclude kv.TiFlash when stmt is not read only
if _, isolationReadContainTiFlash := sessVars.IsolationReadEngines[kv.TiFlash]; isolationReadContainTiFlash && !IsReadOnly(stmtAst.Stmt, sessVars) {
Expand All @@ -309,6 +309,41 @@ func generateNewPlan(ctx context.Context, sctx sessionctx.Context, isNonPrepared
return p, names, err
}

// checkPlanCacheability checks whether this plan is cacheable and set to skip plan cache if it's uncacheable.
func checkPlanCacheability(sctx sessionctx.Context, p Plan, paramNum int) {
stmtCtx := sctx.GetSessionVars().StmtCtx
var pp PhysicalPlan
switch x := p.(type) {
case *Insert:
pp = x.SelectPlan
case *Update:
pp = x.SelectPlan
case *Delete:
pp = x.SelectPlan
case PhysicalPlan:
pp = x
default:
stmtCtx.SetSkipPlanCache(errors.Errorf("skip plan-cache: unexpected un-cacheable plan %v", p.ExplainID().String()))
return
}
if pp == nil { // simple DML statements
return
}

if useTiFlash(pp) {
stmtCtx.SetSkipPlanCache(errors.Errorf("skip plan-cache: TiFlash plan is un-cacheable"))
return
}

// We only cache the tableDual plan when the number of parameters are zero.
if containTableDual(pp) && paramNum > 0 {
stmtCtx.SetSkipPlanCache(errors.New("skip plan-cache: get a TableDual plan"))
return
}

// TODO: plans accessing MVIndex are un-cacheable
}

// RebuildPlan4CachedPlan will rebuild this plan under current user parameters.
func RebuildPlan4CachedPlan(p Plan) error {
sc := p.SCtx().GetSessionVars().StmtCtx
Expand Down Expand Up @@ -678,17 +713,13 @@ func tryCachePointPlan(_ context.Context, sctx sessionctx.Context,
return err
}

func containTableDual(p Plan) bool {
func containTableDual(p PhysicalPlan) bool {
_, isTableDual := p.(*PhysicalTableDual)
if isTableDual {
return true
}
physicalPlan, ok := p.(PhysicalPlan)
if !ok {
return false
}
childContainTableDual := false
for _, child := range physicalPlan.Children() {
for _, child := range p.Children() {
childContainTableDual = childContainTableDual || containTableDual(child)
}
return childContainTableDual
Expand Down

0 comments on commit bdc6f4b

Please sign in to comment.