Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

planner: skip caching too large plan for plan cache #42976

Merged
merged 1 commit into from Apr 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
58 changes: 58 additions & 0 deletions planner/core/plan_cache_test.go
Expand Up @@ -388,6 +388,64 @@ func TestNonPreparedPlanCacheSQLMode(t *testing.T) {
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
}

func TestPreparedPlanCacheLargePlan(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`use test`)
tk.MustExec("create table t(a int, b int, c varchar(2048))")

baseSQL := "select * from t, t t1 where t1.c=space(2048) and t.c=space(2048) and t.a=t1.b"
var baseSQLs []string
for i := 0; i < 30; i++ {
baseSQLs = append(baseSQLs, baseSQL)
}

tk.MustExec("prepare st from '" + strings.Join(baseSQLs[:15], " union all ") + "'")
tk.MustExec("execute st")
tk.MustExec("execute st")
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1")) // less than 2MB threshold

tk.MustExec("prepare st from '" + strings.Join(baseSQLs[:30], " union all ") + "'")
tk.MustExec("execute st")
tk.MustExec("execute st")
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0")) // large than 2MB threshold
}

func TestPreparedPlanCacheLongInList(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`use test`)
tk.MustExec("create table t(a int, b int)")

genInList := func(l int) string {
var elements []string
for i := 0; i < l; i++ {
elements = append(elements, fmt.Sprintf("%v", i))
}
return "(" + strings.Join(elements, ",") + ")"
}

tk.MustExec(fmt.Sprintf(`prepare st_99 from 'select * from t where a in %v'`, genInList(99)))
tk.MustExec(`execute st_99`)
tk.MustExec(`execute st_99`)
tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("1"))

tk.MustExec(fmt.Sprintf(`prepare st_101 from 'select * from t where a in %v'`, genInList(101)))
tk.MustExec(`execute st_101`)
tk.MustExec(`execute st_101`)
tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("0"))

tk.MustExec(fmt.Sprintf(`prepare st_49_50 from 'select * from t where a in %v and b in %v'`, genInList(49), genInList(50)))
tk.MustExec(`execute st_49_50`)
tk.MustExec(`execute st_49_50`)
tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("1"))

tk.MustExec(fmt.Sprintf(`prepare st_49_52 from 'select * from t where a in %v and b in %v'`, genInList(49), genInList(52)))
tk.MustExec(`execute st_49_52`)
tk.MustExec(`execute st_49_52`)
tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("0"))
}

func TestPreparedPlanCacheStats(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
Expand Down
21 changes: 17 additions & 4 deletions planner/core/plan_cacheable_checker.go
Expand Up @@ -29,6 +29,7 @@ import (
driver "github.com/pingcap/tidb/types/parser_driver"
"github.com/pingcap/tidb/util/filter"
"github.com/pingcap/tidb/util/logutil"
"github.com/pingcap/tidb/util/size"
"go.uber.org/zap"
)

Expand All @@ -51,9 +52,10 @@ func CacheableWithCtx(sctx sessionctx.Context, node ast.Node, is infoschema.Info
return false, "not a SELECT/UPDATE/INSERT/DELETE/SET statement"
}
checker := cacheableChecker{
sctx: sctx,
cacheable: true,
schema: is,
sctx: sctx,
cacheable: true,
schema: is,
sumInListLen: 0,
}
node.Accept(&checker)
return checker.cacheable, checker.reason
Expand All @@ -65,6 +67,8 @@ type cacheableChecker struct {
cacheable bool
schema infoschema.InfoSchema
reason string // reason why cannot use plan-cache

sumInListLen int // the accumulated number of elements in all in-lists
}

// Enter implements Visitor interface.
Expand Down Expand Up @@ -114,7 +118,13 @@ func (checker *cacheableChecker) Enter(in ast.Node) (out ast.Node, skipChildren
return in, true
}
}

case *ast.PatternInExpr:
checker.sumInListLen += len(node.List)
if checker.sumInListLen > 100 { // to save memory
checker.cacheable = false
checker.reason = "too many values in in-list (more than 100)"
return in, true
}
case *ast.VariableExpr:
checker.cacheable = false
checker.reason = "query has user-defined variables is un-cacheable"
Expand Down Expand Up @@ -594,6 +604,9 @@ func isPlanCacheable(sctx sessionctx.Context, p Plan, paramNum, limitParamNum in
if hasSubQuery && !sctx.GetSessionVars().EnablePlanCacheForSubquery {
return false, "the switch 'tidb_enable_plan_cache_for_subquery' is off"
}
if uint64(pp.MemoryUsage()) > 2*size.MB { // to save memory
return false, "plan is too large(>2MB)"
}
return isPhysicalPlanCacheable(sctx, pp, paramNum, limitParamNum, false)
}

Expand Down