Skip to content

Commit

Permalink
*: introduce LRUCache with limited memory for statistics (#18788)
Browse files Browse the repository at this point in the history
  • Loading branch information
miamia0 authored Oct 15, 2020
1 parent 9958875 commit cd16de8
Show file tree
Hide file tree
Showing 27 changed files with 926 additions and 216 deletions.
7 changes: 7 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ const (
DefMaxIndexLength = 3072
// DefMaxOfMaxIndexLength is the maximum index length(in bytes) for TiDB v3.0.7 and previous version.
DefMaxOfMaxIndexLength = 3072 * 4
// DefMinQuotaStatistics is the minimum statistic memory quota(in bytes).
DefMinQuotaStatistics = 32 << 30
// DefPort is the default port of TiDB
DefPort = 4000
// DefStatusPort is the default status port of TiDB
Expand Down Expand Up @@ -98,6 +100,7 @@ type Config struct {
TempStoragePath string `toml:"tmp-storage-path" json:"tmp-storage-path"`
OOMAction string `toml:"oom-action" json:"oom-action"`
MemQuotaQuery int64 `toml:"mem-quota-query" json:"mem-quota-query"`
MemQuotaStatistics int64 `toml:"mem-quota-statistics" json:"mem-quota-statistics"`
NestedLoopJoinCacheCapacity int64 `toml:"nested-loop-join-cache-capacity" json:"nested-loop-join-cache-capacity"`
// TempStorageQuota describe the temporary storage Quota during query exector when OOMUseTmpStorage is enabled
// If the quota exceed the capacity of the TempStoragePath, the tidb-server would exit with fatal error
Expand Down Expand Up @@ -614,6 +617,7 @@ var defaultConf = Config{
TempStoragePath: tempStorageDirName,
OOMAction: OOMActionCancel,
MemQuotaQuery: 1 << 30,
MemQuotaStatistics: 32 << 30,
NestedLoopJoinCacheCapacity: 20971520,
EnableStreaming: false,
EnableBatchDML: false,
Expand Down Expand Up @@ -952,6 +956,9 @@ func (c *Config) Valid() error {
if c.PreparedPlanCache.MemoryGuardRatio < 0 || c.PreparedPlanCache.MemoryGuardRatio > 1 {
return fmt.Errorf("memory-guard-ratio in [prepared-plan-cache] must be NOT less than 0 and more than 1")
}
if c.MemQuotaStatistics < DefMinQuotaStatistics {
return fmt.Errorf("memory-quota-statistics should be greater than %dB", DefMinQuotaStatistics)
}
if len(c.IsolationRead.Engines) < 1 {
return fmt.Errorf("the number of [isolation-read]engines for isolation read should be at least 1")
}
Expand Down
4 changes: 4 additions & 0 deletions config/config.toml.example
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ token-limit = 1000
# The maximum memory available for a single SQL statement. Default: 1GB
mem-quota-query = 1073741824

# The maximum memory limitation for statistics. Default: 32GB
# This value must not be less than 32GB.
mem-quota-statistics = 34359738368

# The maximum number available of a NLJ cache for a single SQL statement. Default: 20MB
nested-loop-join-cache-capacity = 20971520

Expand Down
2 changes: 2 additions & 0 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ server-version = "test_version"
repair-mode = true
max-server-connections = 200
mem-quota-query = 10000
mem-quota-statistics = 10000
nested-loop-join-cache-capacity = 100
max-index-length = 3080
skip-register-to-dashboard = true
Expand Down Expand Up @@ -258,6 +259,7 @@ spilled-file-encryption-method = "plaintext"
c.Assert(conf.RepairMode, Equals, true)
c.Assert(conf.MaxServerConnections, Equals, uint32(200))
c.Assert(conf.MemQuotaQuery, Equals, int64(10000))
c.Assert(conf.MemQuotaStatistics, Equals, int64(10000))
c.Assert(conf.NestedLoopJoinCacheCapacity, Equals, int64(100))
c.Assert(conf.IsolationRead.Engines, DeepEquals, []string{"tiflash"})
c.Assert(conf.MaxIndexLength, Equals, 3080)
Expand Down
3 changes: 2 additions & 1 deletion config/config_util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ func (s *testConfigSuite) TestMergeConfigItems(c *C) {
newConf.Performance.PseudoEstimateRatio = 123
newConf.OOMAction = "panic"
newConf.MemQuotaQuery = 123
newConf.MemQuotaStatistics = 123
newConf.TiKVClient.StoreLimit = 123

// rejected
Expand All @@ -66,7 +67,7 @@ func (s *testConfigSuite) TestMergeConfigItems(c *C) {

as, rs := MergeConfigItems(oldConf, newConf)
c.Assert(len(as), Equals, 10)
c.Assert(len(rs), Equals, 3)
c.Assert(len(rs), Equals, 4)
for _, a := range as {
_, ok := dynamicConfigItems[a]
c.Assert(ok, IsTrue)
Expand Down
4 changes: 2 additions & 2 deletions executor/infoschema_reader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ func (s *testInfoschemaTableSerialSuite) TestDataForTableStatsField(c *C) {
defer func() { executor.TableStatsCacheExpiry = oldExpiryTime }()
do := s.dom
h := do.StatsHandle()
h.Clear()
h.Clear4Test()
is := do.InfoSchema()
tk := testkit.NewTestKit(c, s.store)

Expand Down Expand Up @@ -414,7 +414,7 @@ func (s *testInfoschemaTableSerialSuite) TestPartitionsTable(c *C) {
defer func() { executor.TableStatsCacheExpiry = oldExpiryTime }()
do := s.dom
h := do.StatsHandle()
h.Clear()
h.Clear4Test()
is := do.InfoSchema()

tk := testkit.NewTestKit(c, s.store)
Expand Down
2 changes: 1 addition & 1 deletion executor/simple_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -564,7 +564,7 @@ func (s *testSuite3) TestDropStats(c *C) {
c.Assert(err, IsNil)
tableInfo := tbl.Meta()
h := do.StatsHandle()
h.Clear()
h.Clear4Test()
testKit.MustExec("analyze table t")
statsTbl := h.GetTableStats(tableInfo)
c.Assert(statsTbl.Pseudo, IsFalse)
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,9 @@ github.com/coocood/bbloom v0.0.0-20190830030839-58deb6228d64 h1:W1SHiII3e0jVwvaQ
github.com/coocood/bbloom v0.0.0-20190830030839-58deb6228d64/go.mod h1:F86k/6c7aDUdwSUevnLpHS/3Q9hzYCE99jGk2xsHnt0=
github.com/coocood/rtutil v0.0.0-20190304133409-c84515f646f2 h1:NnLfQ77q0G4k2Of2c1ceQ0ec6MkLQyDp+IGdVM0D8XM=
github.com/coocood/rtutil v0.0.0-20190304133409-c84515f646f2/go.mod h1:7qG7YFnOALvsx6tKTNmQot8d7cGFXM9TidzvRFLWYwM=
github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible h1:jFneRYjIvLMLhDLCzuTuU4rSJUjRplcJQ7pD7MnhC04=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
Expand Down Expand Up @@ -566,10 +568,12 @@ github.com/uber/jaeger-lib v2.4.0+incompatible h1:fY7QsGQWiCt8pajv4r7JEvmATdCVaW
github.com/uber/jaeger-lib v2.4.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/ugorji/go v1.1.5-pre/go.mod h1:FwP/aQVg39TXzItUBMwnWp9T9gPQnXw4Poh4/oBQZ/0=
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go/codec v0.0.0-20181022190402-e5e69e061d4f/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/ugorji/go/codec v1.1.5-pre/go.mod h1:tULtS6Gy1AE1yCENaw4Vb//HLH5njI2tfCQDUqRd8fI=
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/unrolled/render v0.0.0-20171102162132-65450fb6b2d3/go.mod h1:tu82oB5W2ykJRVioYsB+IQKcft7ryBr7w12qMBUPyXg=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
Expand Down
4 changes: 2 additions & 2 deletions planner/core/cbo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,7 @@ func (s *testAnalyzeSuite) TestNullCount(c *C) {
testKit.MustQuery(input[i]).Check(testkit.Rows(output[i]...))
}
h := dom.StatsHandle()
h.Clear()
h.Clear4Test()
c.Assert(h.Update(dom.InfoSchema()), IsNil)
for i := 2; i < 4; i++ {
s.testData.OnRecord(func() {
Expand Down Expand Up @@ -552,7 +552,7 @@ func (s *testAnalyzeSuite) TestInconsistentEstimation(c *C) {
tk.MustExec("analyze table t with 2 buckets")
// Force using the histogram to estimate.
tk.MustExec("update mysql.stats_histograms set stats_ver = 0")
dom.StatsHandle().Clear()
dom.StatsHandle().Clear4Test()
dom.StatsHandle().Update(dom.InfoSchema())
// Using the histogram (a, b) to estimate `a = 5` will get 1.22, while using the CM Sketch to estimate
// the `a = 5 and c = 5` will get 10, it is not consistent.
Expand Down
2 changes: 1 addition & 1 deletion planner/core/find_best_task.go
Original file line number Diff line number Diff line change
Expand Up @@ -1222,7 +1222,7 @@ func getColumnRangeCounts(sc *stmtctx.StatementContext, colID int64, ranges []*r
for i, ran := range ranges {
if idxID >= 0 {
idxHist := histColl.Indices[idxID]
if idxHist == nil || idxHist.IsInvalid(false) {
if idxHist == nil || idxHist.IsInvalid(sc, false) {
return nil, false
}
count, err = histColl.GetRowCountByIndexRanges(sc, idxID, []*ranger.Range{ran})
Expand Down
37 changes: 9 additions & 28 deletions planner/core/planbuilder.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ package core
import (
"bytes"
"context"
"encoding/binary"
"fmt"
"strings"
"time"
Expand All @@ -42,7 +41,7 @@ import (
"github.com/pingcap/tidb/store/tikv"
"github.com/pingcap/tidb/table"
"github.com/pingcap/tidb/types"
driver "github.com/pingcap/tidb/types/parser_driver"
"github.com/pingcap/tidb/types/parser_driver"
util2 "github.com/pingcap/tidb/util"
"github.com/pingcap/tidb/util/chunk"
"github.com/pingcap/tidb/util/execdetails"
Expand Down Expand Up @@ -1688,43 +1687,25 @@ func (b *PlanBuilder) buildAnalyzeAllIndex(as *ast.AnalyzeTableStmt, opts map[as
return p, nil
}

var cmSketchSizeLimit = kv.TxnEntrySizeLimit / binary.MaxVarintLen32

var analyzeOptionLimit = map[ast.AnalyzeOptionType]uint64{
ast.AnalyzeOptNumBuckets: 1024,
ast.AnalyzeOptNumTopN: 1024,
ast.AnalyzeOptCMSketchWidth: cmSketchSizeLimit,
ast.AnalyzeOptCMSketchDepth: cmSketchSizeLimit,
ast.AnalyzeOptNumSamples: 100000,
}

var analyzeOptionDefault = map[ast.AnalyzeOptionType]uint64{
ast.AnalyzeOptNumBuckets: 256,
ast.AnalyzeOptNumTopN: 20,
ast.AnalyzeOptCMSketchWidth: 2048,
ast.AnalyzeOptCMSketchDepth: 5,
ast.AnalyzeOptNumSamples: 10000,
}

func handleAnalyzeOptions(opts []ast.AnalyzeOpt) (map[ast.AnalyzeOptionType]uint64, error) {
optMap := make(map[ast.AnalyzeOptionType]uint64, len(analyzeOptionDefault))
for key, val := range analyzeOptionDefault {
optMap := make(map[ast.AnalyzeOptionType]uint64, len(statistics.AnalyzeOptionDefault))
for key, val := range statistics.AnalyzeOptionDefault {
optMap[key] = val
}
for _, opt := range opts {
if opt.Type == ast.AnalyzeOptNumTopN {
if opt.Value > analyzeOptionLimit[opt.Type] {
return nil, errors.Errorf("value of analyze option %s should not larger than %d", ast.AnalyzeOptionString[opt.Type], analyzeOptionLimit[opt.Type])
if opt.Value > statistics.AnalyzeOptionLimit[opt.Type] {
return nil, errors.Errorf("value of analyze option %s should not larger than %d", ast.AnalyzeOptionString[opt.Type], statistics.AnalyzeOptionLimit[opt.Type])
}
} else {
if opt.Value == 0 || opt.Value > analyzeOptionLimit[opt.Type] {
return nil, errors.Errorf("value of analyze option %s should be positive and not larger than %d", ast.AnalyzeOptionString[opt.Type], analyzeOptionLimit[opt.Type])
if opt.Value == 0 || opt.Value > statistics.AnalyzeOptionLimit[opt.Type] {
return nil, errors.Errorf("value of analyze option %s should be positive and not larger than %d", ast.AnalyzeOptionString[opt.Type], statistics.AnalyzeOptionLimit[opt.Type])
}
}
optMap[opt.Type] = opt.Value
}
if optMap[ast.AnalyzeOptCMSketchWidth]*optMap[ast.AnalyzeOptCMSketchDepth] > cmSketchSizeLimit {
return nil, errors.Errorf("cm sketch size(depth * width) should not larger than %d", cmSketchSizeLimit)
if optMap[ast.AnalyzeOptCMSketchWidth]*optMap[ast.AnalyzeOptCMSketchDepth] > statistics.CMSketchSizeLimit {
return nil, errors.Errorf("cm sketch size(depth * width) should not larger than %d", statistics.CMSketchSizeLimit)
}
return optMap, nil
}
Expand Down
6 changes: 5 additions & 1 deletion sessionctx/variable/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -858,6 +858,7 @@ func NewSessionVars() *SessionVars {
}
vars.MemQuota = MemQuota{
MemQuotaQuery: config.GetGlobalConfig().MemQuotaQuery,
MemQuotaStatistics: config.GetGlobalConfig().MemQuotaStatistics,
NestedLoopJoinCacheCapacity: config.GetGlobalConfig().NestedLoopJoinCacheCapacity,

// The variables below do not take any effect anymore, it's remaining for compatibility.
Expand Down Expand Up @@ -1303,6 +1304,8 @@ func (s *SessionVars) SetSystemVar(name string, val string) error {
s.InitChunkSize = tidbOptPositiveInt32(val, DefInitChunkSize)
case TIDBMemQuotaQuery:
s.MemQuotaQuery = tidbOptInt64(val, config.GetGlobalConfig().MemQuotaQuery)
case TIDBMemQuotaStatistics:
s.MemQuotaStatistics = tidbOptInt64(val, config.GetGlobalConfig().MemQuotaStatistics)
case TIDBNestedLoopJoinCacheCapacity:
s.NestedLoopJoinCacheCapacity = tidbOptInt64(val, config.GetGlobalConfig().NestedLoopJoinCacheCapacity)
case TIDBMemQuotaHashJoin:
Expand Down Expand Up @@ -1728,7 +1731,8 @@ func (c *Concurrency) UnionConcurrency() int {
type MemQuota struct {
// MemQuotaQuery defines the memory quota for a query.
MemQuotaQuery int64

// MemQuotaStatistics defines the memory quota for the statistic Cache.
MemQuotaStatistics int64
// NestedLoopJoinCacheCapacity defines the memory capacity for apply cache.
NestedLoopJoinCacheCapacity int64

Expand Down
3 changes: 3 additions & 0 deletions sessionctx/variable/sysvar.go
Original file line number Diff line number Diff line change
Expand Up @@ -683,9 +683,11 @@ var defaultSysVars = []*SysVar{
{Scope: ScopeGlobal | ScopeSession, Name: TiDBMaxChunkSize, Value: strconv.Itoa(DefMaxChunkSize)},
{Scope: ScopeGlobal | ScopeSession, Name: TiDBAllowBatchCop, Value: strconv.Itoa(DefTiDBAllowBatchCop)},
{Scope: ScopeGlobal | ScopeSession, Name: TiDBInitChunkSize, Value: strconv.Itoa(DefInitChunkSize)},

{Scope: ScopeGlobal | ScopeSession, Name: TiDBEnableCascadesPlanner, Value: "0", Type: TypeBool},
{Scope: ScopeGlobal | ScopeSession, Name: TiDBEnableIndexMerge, Value: "0", Type: TypeBool},
{Scope: ScopeSession, Name: TIDBMemQuotaQuery, Value: strconv.FormatInt(config.GetGlobalConfig().MemQuotaQuery, 10), Type: TypeInt, MinValue: -1, MaxValue: math.MaxInt64},
{Scope: ScopeGlobal, Name: TIDBMemQuotaStatistics, Value: strconv.FormatInt(config.GetGlobalConfig().MemQuotaStatistics, 10), Type: TypeInt, MinValue: int64(32 << 30), MaxValue: math.MaxInt64},
{Scope: ScopeSession, Name: TIDBMemQuotaHashJoin, Value: strconv.FormatInt(DefTiDBMemQuotaHashJoin, 10), Type: TypeInt, MinValue: -1, MaxValue: math.MaxInt64},
{Scope: ScopeSession, Name: TIDBMemQuotaMergeJoin, Value: strconv.FormatInt(DefTiDBMemQuotaMergeJoin, 10), Type: TypeInt, MinValue: -1, MaxValue: math.MaxInt64},
{Scope: ScopeSession, Name: TIDBMemQuotaSort, Value: strconv.FormatInt(DefTiDBMemQuotaSort, 10), Type: TypeInt, MinValue: -1, MaxValue: math.MaxInt64},
Expand All @@ -695,6 +697,7 @@ var defaultSysVars = []*SysVar{
{Scope: ScopeSession, Name: TIDBMemQuotaNestedLoopApply, Value: strconv.FormatInt(DefTiDBMemQuotaNestedLoopApply, 10), Type: TypeInt, MinValue: -1, MaxValue: math.MaxInt64},
{Scope: ScopeSession, Name: TiDBEnableStreaming, Value: "0", Type: TypeBool},
{Scope: ScopeSession, Name: TiDBEnableChunkRPC, Value: "1", Type: TypeBool},

{Scope: ScopeSession, Name: TxnIsolationOneShot, Value: ""},
{Scope: ScopeGlobal | ScopeSession, Name: TiDBEnableTablePartition, Value: "on"},
{Scope: ScopeGlobal | ScopeSession, Name: TiDBHashJoinConcurrency, Value: strconv.Itoa(DefTiDBHashJoinConcurrency)},
Expand Down
1 change: 1 addition & 0 deletions sessionctx/variable/tidb_vars.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ const (
// The following session variables controls the memory quota during query execution.
// "tidb_mem_quota_query": control the memory quota of a query.
TIDBMemQuotaQuery = "tidb_mem_quota_query" // Bytes.
TIDBMemQuotaStatistics = "tidb_mem_quota_statistics"
TIDBNestedLoopJoinCacheCapacity = "tidb_nested_loop_join_cache_capacity"
// TODO: remove them below sometime, it should have only one Quota(TIDBMemQuotaQuery).
TIDBMemQuotaHashJoin = "tidb_mem_quota_hashjoin" // Bytes.
Expand Down
1 change: 1 addition & 0 deletions sessionctx/variable/varsutil_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ func (s *testVarsutilSuite) TestNewSessionVars(c *C) {
c.Assert(vars.MaxChunkSize, Equals, DefMaxChunkSize)
c.Assert(vars.DMLBatchSize, Equals, DefDMLBatchSize)
c.Assert(vars.MemQuotaQuery, Equals, config.GetGlobalConfig().MemQuotaQuery)
c.Assert(vars.MemQuotaStatistics, Equals, config.GetGlobalConfig().MemQuotaStatistics)
c.Assert(vars.MemQuotaHashJoin, Equals, int64(DefTiDBMemQuotaHashJoin))
c.Assert(vars.MemQuotaMergeJoin, Equals, int64(DefTiDBMemQuotaMergeJoin))
c.Assert(vars.MemQuotaSort, Equals, int64(DefTiDBMemQuotaSort))
Expand Down
Loading

0 comments on commit cd16de8

Please sign in to comment.