diff --git a/config/config.go b/config/config.go index b0620728eaf91..ab9dfde5dcb34 100644 --- a/config/config.go +++ b/config/config.go @@ -44,10 +44,6 @@ import ( // Config number limitations const ( MaxLogFileSize = 4096 // MB - // DefTxnEntrySizeLimit is the default value of TxnEntrySizeLimit. - DefTxnEntrySizeLimit = 6 * 1024 * 1024 - // DefTxnTotalSizeLimit is the default value of TxnTxnTotalSizeLimit. - DefTxnTotalSizeLimit = 100 * 1024 * 1024 // DefMaxIndexLength is the maximum index length(in bytes). This value is consistent with MySQL. DefMaxIndexLength = 3072 // DefMaxOfMaxIndexLength is the maximum index length(in bytes) for TiDB v3.0.7 and previous version. @@ -606,8 +602,6 @@ type Performance struct { PseudoEstimateRatio float64 `toml:"pseudo-estimate-ratio" json:"pseudo-estimate-ratio"` ForcePriority string `toml:"force-priority" json:"force-priority"` BindInfoLease string `toml:"bind-info-lease" json:"bind-info-lease"` - TxnEntrySizeLimit uint64 `toml:"txn-entry-size-limit" json:"txn-entry-size-limit"` - TxnTotalSizeLimit uint64 `toml:"txn-total-size-limit" json:"txn-total-size-limit"` TCPKeepAlive bool `toml:"tcp-keep-alive" json:"tcp-keep-alive"` TCPNoDelay bool `toml:"tcp-no-delay" json:"tcp-no-delay"` CrossJoin bool `toml:"cross-join" json:"cross-join"` @@ -628,8 +622,10 @@ type Performance struct { // to support the upgrade process. They can be removed in future. // CommitterConcurrency, RunAutoAnalyze unused since bootstrap v90 - CommitterConcurrency int `toml:"committer-concurrency" json:"committer-concurrency"` - RunAutoAnalyze bool `toml:"run-auto-analyze" json:"run-auto-analyze"` + CommitterConcurrency int `toml:"committer-concurrency" json:"committer-concurrency"` + RunAutoAnalyze bool `toml:"run-auto-analyze" json:"run-auto-analyze"` + TxnEntrySizeLimit uint64 `toml:"txn-entry-size-limit" json:"txn-entry-size-limit"` + TxnTotalSizeLimit uint64 `toml:"txn-total-size-limit" json:"txn-total-size-limit"` } // PlanCache is the PlanCache section of the config. @@ -835,8 +831,6 @@ var defaultConf = Config{ PseudoEstimateRatio: 0.8, ForcePriority: "NO_PRIORITY", BindInfoLease: "3s", - TxnEntrySizeLimit: DefTxnEntrySizeLimit, - TxnTotalSizeLimit: DefTxnTotalSizeLimit, DistinctAggPushDown: false, ProjectionPushDown: false, MaxTxnTTL: defTiKVCfg.MaxTxnTTL, // 1hour @@ -953,6 +947,8 @@ var deprecatedConfig = map[string]struct{}{ "enable-batch-dml": {}, // use tidb_enable_batch_dml "mem-quota-query": {}, "log.query-log-max-len": {}, + "performance.txn-total-size-limit": {}, // use tidb_txn_total_size_limit + "performance.txn-entry-size-limit": {}, // use tidb_txn_entry_size_limit "performance.committer-concurrency": {}, "experimental.enable-global-kill": {}, "performance.run-auto-analyze": {}, //use tidb_enable_auto_analyze @@ -1118,10 +1114,6 @@ func (c *Config) Valid() error { return err } - if c.Performance.TxnTotalSizeLimit > 1<<40 { - return fmt.Errorf("txn-total-size-limit should be less than %d", 1<<40) - } - if c.Instance.MemoryUsageAlarmRatio > 1 || c.Instance.MemoryUsageAlarmRatio < 0 { return fmt.Errorf("tidb_memory_usage_alarm_ratio in [Instance] must be greater than or equal to 0 and less than or equal to 1") } diff --git a/config/config.toml.example b/config/config.toml.example index 0a7307870a0f9..8842ff9060173 100644 --- a/config/config.toml.example +++ b/config/config.toml.example @@ -248,17 +248,6 @@ bind-info-lease = "3s" # Whether support pushing down aggregation with distinct to cop task distinct-agg-push-down = false -# The limitation of the size in byte for the entries in one transaction. -# If using TiKV as the storage, the entry represents a key/value pair. -# NOTE: If binlog is enabled with Kafka (e.g. arbiter cluster), -# this value should be less than 1073741824(1G) because this is the maximum size that can be handled by Kafka. -# If binlog is disabled or binlog is enabled without Kafka, this value should be less than 1099511627776(1T). -txn-total-size-limit = 104857600 - -# The limitation of the size in byte for each entry in one transaction. -# NOTE: Increasing this limit may cause performance problems. -txn-entry-size-limit = 6291456 - # max lifetime of transaction ttl manager. max-txn-ttl = 3600000 @@ -386,7 +375,7 @@ capacity-mb = 1000.0 [binlog] # enable to write binlog. # NOTE: If binlog is enabled with Kafka (e.g. arbiter cluster), -# txn-total-size-limit should be less than 1073741824(1G) because this is the maximum size that can be handled by Kafka. +# The sysvar tidb_txn_total_size_limit should be less than 1073741824(1G) because this is the maximum size that can be handled by Kafka. enable = false # WriteTimeout specifies how long it will wait for writing binlog to pump. diff --git a/config/config_test.go b/config/config_test.go index 545f8d4d627b6..0babe521d7a29 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -173,7 +173,6 @@ func TestConfig(t *testing.T) { conf.Binlog.Enable = true conf.Binlog.IgnoreError = true conf.Binlog.Strategy = "hash" - conf.Performance.TxnTotalSizeLimit = 1000 conf.TiKVClient.CommitTimeout = "10s" conf.TiKVClient.RegionCacheTTL = 600 conf.Instance.EnableSlowLog.Store(logutil.DefaultTiDBEnableSlowLog) @@ -222,7 +221,6 @@ stores-refresh-interval = 30 enable-forwarding = true enable-global-kill = true [performance] -txn-total-size-limit=2000 tcp-no-delay = false [tikv-client] commit-timeout="41s" @@ -268,7 +266,6 @@ grpc-max-send-msg-size = 40960 require.Equal(t, "hash", conf.Binlog.Strategy) // Test that the value will be overwritten by the config file. - require.Equal(t, uint64(2000), conf.Performance.TxnTotalSizeLimit) require.True(t, conf.AlterPrimaryKey) require.False(t, conf.Performance.TCPNoDelay) @@ -473,25 +470,6 @@ xkNuJ2BlEGkwWLiRbKy1lNBBFUXKuhh3L/EIY10WTnr3TQzeL6H1 } } -func TestTxnTotalSizeLimitValid(t *testing.T) { - conf := NewConfig() - tests := []struct { - limit uint64 - valid bool - }{ - {4 << 10, true}, - {10 << 30, true}, - {10<<30 + 1, true}, - {1 << 40, true}, - {1<<40 + 1, false}, - } - - for _, tt := range tests { - conf.Performance.TxnTotalSizeLimit = tt.limit - require.Equal(t, tt.valid, conf.Valid() == nil) - } -} - func TestConflictInstanceConfig(t *testing.T) { var expectedNewName string conf := new(Config) diff --git a/executor/seqtest/seq_executor_test.go b/executor/seqtest/seq_executor_test.go index f5663309743e1..bac52d8c35b87 100644 --- a/executor/seqtest/seq_executor_test.go +++ b/executor/seqtest/seq_executor_test.go @@ -935,16 +935,10 @@ func TestCartesianProduct(t *testing.T) { func TestBatchInsertDelete(t *testing.T) { store, clean := testkit.CreateMockStore(t) defer clean() - - originLimit := atomic.LoadUint64(&kv.TxnTotalSizeLimit) - defer func() { - atomic.StoreUint64(&kv.TxnTotalSizeLimit, originLimit) - }() - // Set the limitation to a small value, make it easier to reach the limitation. - atomic.StoreUint64(&kv.TxnTotalSizeLimit, 5500) - tk := testkit.NewTestKit(t, store) tk.MustExec("use test") + tk.MustExec("set global tidb_txn_total_size_limit = 5500") + defer tk.MustExec("set global tidb_txn_total_size_limit = default") tk.MustExec("drop table if exists batch_insert") tk.MustExec("create table batch_insert (c int)") tk.MustExec("drop table if exists batch_insert_on_duplicate") diff --git a/kv/kv.go b/kv/kv.go index 58aecb5195891..d611e66f6f249 100644 --- a/kv/kv.go +++ b/kv/kv.go @@ -23,7 +23,6 @@ import ( deadlockpb "github.com/pingcap/kvproto/pkg/deadlock" "github.com/pingcap/kvproto/pkg/kvrpcpb" "github.com/pingcap/kvproto/pkg/metapb" - "github.com/pingcap/tidb/config" "github.com/pingcap/tidb/parser/model" "github.com/pingcap/tidb/util/memory" "github.com/pingcap/tidb/util/trxevents" @@ -32,6 +31,7 @@ import ( "github.com/tikv/client-go/v2/tikv" "github.com/tikv/client-go/v2/tikvrpc" pd "github.com/tikv/pd/client" + "go.uber.org/atomic" ) // UnCommitIndexKVFlag uses to indicate the index key/value is no need to commit. @@ -46,9 +46,9 @@ const UnCommitIndexKVFlag byte = '1' // Those limits is enforced to make sure the transaction can be well handled by TiKV. var ( // TxnEntrySizeLimit is limit of single entry size (len(key) + len(value)). - TxnEntrySizeLimit uint64 = config.DefTxnEntrySizeLimit + TxnEntrySizeLimit = atomic.NewUint64(6 * 1024 * 1024) //DefTiDBTxnEntrySizeLimit // TxnTotalSizeLimit is limit of the sum of all entry size. - TxnTotalSizeLimit uint64 = config.DefTxnTotalSizeLimit + TxnTotalSizeLimit = atomic.NewUint64(100 * 1024 * 1024) //DefTiDBTxnTotalSizeLimit ) // Getter is the interface for the Get method. diff --git a/planner/core/planbuilder.go b/planner/core/planbuilder.go index 4c83651d02bc4..8bbd8797733d4 100644 --- a/planner/core/planbuilder.go +++ b/planner/core/planbuilder.go @@ -2546,7 +2546,7 @@ func (b *PlanBuilder) buildAnalyzeAllIndex(as *ast.AnalyzeTableStmt, opts map[as } // CMSketchSizeLimit indicates the size limit of CMSketch. -var CMSketchSizeLimit = kv.TxnEntrySizeLimit / binary.MaxVarintLen32 +var CMSketchSizeLimit = kv.TxnEntrySizeLimit.Load() / binary.MaxVarintLen32 var analyzeOptionLimit = map[ast.AnalyzeOptionType]uint64{ ast.AnalyzeOptNumBuckets: 1024, diff --git a/server/server_test.go b/server/server_test.go index 83f13fdd97b5a..ed7d7d7dba741 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -36,7 +36,6 @@ import ( "github.com/pingcap/failpoint" "github.com/pingcap/log" "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/kv" tmysql "github.com/pingcap/tidb/parser/mysql" "github.com/pingcap/tidb/testkit" "github.com/pingcap/tidb/util/versioninfo" @@ -868,17 +867,14 @@ func (cli *testServerClient) runTestLoadData(t *testing.T, server *Server) { "xxx row4_col1 - 900\n" + "xxx row5_col1 - row5_col3") require.NoError(t, err) - - originalTxnTotalSizeLimit := kv.TxnTotalSizeLimit - // If the MemBuffer can't be committed once in each batch, it will return an error like "transaction is too large". - kv.TxnTotalSizeLimit = 10240 - defer func() { kv.TxnTotalSizeLimit = originalTxnTotalSizeLimit }() - // support ClientLocalFiles capability cli.runTestsOnNewDB(t, func(config *mysql.Config) { config.AllowAllFiles = true config.Params["sql_mode"] = "''" }, "LoadData", func(dbt *testkit.DBTestKit) { + // If the MemBuffer can't be committed once in each batch, it will return an error like "transaction is too large". + dbt.MustExec("set global tidb_txn_total_size_limit = 10240") + defer dbt.MustExec("set global tidb_txn_total_size_limit = default") dbt.MustExec("set @@tidb_dml_batch_size = 3") dbt.MustExec("create table test (a varchar(255), b varchar(255) default 'default value', c int not null auto_increment, primary key(c))") dbt.MustExec("create view v1 as select 1") diff --git a/session/bootstrap.go b/session/bootstrap.go index 23b7e19244181..6903e6cb05e2e 100644 --- a/session/bootstrap.go +++ b/session/bootstrap.go @@ -615,7 +615,7 @@ const ( version88 = 88 // version89 adds the tables mysql.advisory_locks version89 = 89 - // version90 converts enable-batch-dml, mem-quota-query, query-log-max-len, committer-concurrency, run-auto-analyze, and oom-action to a sysvar + // version90 converts enable-batch-dml, mem-quota-query, query-log-max-len, committer-concurrency, run-auto-analyze,txn-total-size-limit , txn-entry-size-limit and oom-action to a sysvar version90 = 90 // version91 converts prepared-plan-cache to sysvars version91 = 91 @@ -1859,6 +1859,10 @@ func upgradeToVer90(s Session, ver int64) { importConfigOption(s, "run-auto-analyze", variable.TiDBEnableAutoAnalyze, valStr) valStr = config.GetGlobalConfig().OOMAction importConfigOption(s, "oom-action", variable.TiDBMemOOMAction, valStr) + valStr = fmt.Sprint(config.GetGlobalConfig().Performance.TxnEntrySizeLimit) + importConfigOption(s, "txn-entry-size-limit", variable.TiDBTxnEntrySizeLimit, valStr) + valStr = fmt.Sprint(config.GetGlobalConfig().Performance.TxnTotalSizeLimit) + importConfigOption(s, "txn-total-size-limit", variable.TiDBTxnTotalSizeLimit, valStr) } func upgradeToVer91(s Session, ver int64) { diff --git a/session/session.go b/session/session.go index decee805527d9..ec3ab29d8b9b9 100644 --- a/session/session.go +++ b/session/session.go @@ -874,7 +874,7 @@ func (s *session) doCommitWithRetry(ctx context.Context) error { zap.String("txn", s.txn.GoString())) // Transactions will retry 2 ~ commitRetryLimit times. // We make larger transactions retry less times to prevent cluster resource outage. - txnSizeRate := float64(txnSize) / float64(kv.TxnTotalSizeLimit) + txnSizeRate := float64(txnSize) / float64(kv.TxnTotalSizeLimit.Load()) maxRetryCount := commitRetryLimit - int64(float64(commitRetryLimit-1)*txnSizeRate) err = s.retry(ctx, uint(maxRetryCount)) } else if !errIsNoisy(err) { diff --git a/sessionctx/variable/sysvar.go b/sessionctx/variable/sysvar.go index f4c169aa22bfc..dea6bcbbb4572 100644 --- a/sessionctx/variable/sysvar.go +++ b/sessionctx/variable/sysvar.go @@ -421,6 +421,18 @@ var defaultSysVars = []*SysVar{ }}, /* The system variables below have GLOBAL scope */ + {Scope: ScopeGlobal, Name: TiDBTxnEntrySizeLimit, Value: strconv.FormatInt(DefTiDBTxnEntrySizeLimit, 10), Type: TypeInt, MinValue: 1 << 10, MaxValue: 120 << 20, GetGlobal: func(sv *SessionVars) (string, error) { + return strconv.FormatUint(kv.TxnEntrySizeLimit.Load(), 10), nil + }, SetGlobal: func(s *SessionVars, val string) error { + kv.TxnEntrySizeLimit.Store(uint64(TidbOptInt64(val, DefTiDBTxnEntrySizeLimit))) + return nil + }}, + {Scope: ScopeGlobal, Name: TiDBTxnTotalSizeLimit, Value: strconv.FormatInt(DefTiDBTxnTotalSizeLimit, 10), Type: TypeInt, MinValue: 1 << 10, MaxValue: 1 << 40, GetGlobal: func(sv *SessionVars) (string, error) { + return strconv.FormatUint(kv.TxnTotalSizeLimit.Load(), 10), nil + }, SetGlobal: func(s *SessionVars, val string) error { + kv.TxnTotalSizeLimit.Store(uint64(TidbOptInt64(val, DefTiDBTxnTotalSizeLimit))) + return nil + }}, {Scope: ScopeGlobal, Name: MaxPreparedStmtCount, Value: strconv.FormatInt(DefMaxPreparedStmtCount, 10), Type: TypeInt, MinValue: -1, MaxValue: 1048576}, {Scope: ScopeGlobal, Name: InitConnect, Value: ""}, /* TiDB specific variables */ diff --git a/sessionctx/variable/sysvar_test.go b/sessionctx/variable/sysvar_test.go index 6c62a697e6d5b..a23ed9663b5b1 100644 --- a/sessionctx/variable/sysvar_test.go +++ b/sessionctx/variable/sysvar_test.go @@ -961,6 +961,56 @@ func TestTiDBQueryLogMaxLen(t *testing.T) { require.NoError(t, err) } +func TestTiDBTxnTotalSizeLimit(t *testing.T) { + sv := GetSysVar(TiDBTxnTotalSizeLimit) + vars := NewSessionVars() + + newVal := 10485760 + val, err := sv.Validate(vars, fmt.Sprintf("%d", newVal), ScopeGlobal) + require.Equal(t, "10485760", val) + require.NoError(t, err) + + // out of range + newVal = 1099511627777 + expected := 1099511627776 + val, err = sv.Validate(vars, fmt.Sprintf("%d", newVal), ScopeGlobal) + // expected to truncate + require.Equal(t, fmt.Sprintf("%d", expected), val) + require.NoError(t, err) + + // min value out of range + newVal = 1022 + expected = 1024 + val, err = sv.Validate(vars, fmt.Sprintf("%d", newVal), ScopeGlobal) + // expected to set to min value + require.Equal(t, fmt.Sprintf("%d", expected), val) + require.NoError(t, err) +} +func TestTiDBTxnEntrySizeLimit(t *testing.T) { + sv := GetSysVar(TiDBTxnEntrySizeLimit) + vars := NewSessionVars() + + newVal := 10485760 + val, err := sv.Validate(vars, fmt.Sprintf("%d", newVal), ScopeGlobal) + require.Equal(t, "10485760", val) + require.NoError(t, err) + + // Max Value out of range + newVal = 125829121 + expected := 125829120 + val, err = sv.Validate(vars, fmt.Sprintf("%d", newVal), ScopeGlobal) + // expected to truncate + require.Equal(t, fmt.Sprintf("%d", expected), val) + require.NoError(t, err) + + // min value out of range + newVal = 1022 + expected = 1024 + val, err = sv.Validate(vars, fmt.Sprintf("%d", newVal), ScopeGlobal) + // expected to set to min value + require.Equal(t, fmt.Sprintf("%d", expected), val) + require.NoError(t, err) +} func TestTiDBCommitterConcurrency(t *testing.T) { sv := GetSysVar(TiDBCommitterConcurrency) vars := NewSessionVars() diff --git a/sessionctx/variable/tidb_vars.go b/sessionctx/variable/tidb_vars.go index 56f43aba36ebe..fd9b1d302c91e 100644 --- a/sessionctx/variable/tidb_vars.go +++ b/sessionctx/variable/tidb_vars.go @@ -652,6 +652,10 @@ const ( // TiDB vars that have only global scope const ( + // TiDBTxnEntrySizeLimit is the size limit of a single row of data in TiDB + TiDBTxnEntrySizeLimit = "tidb_txn_entry_size_limit" + // TiDBTxnTotalSizeLimit is the size limit of a single transaction + TiDBTxnTotalSizeLimit = "tidb_txn_total_size_limit" // TiDBGCEnable turns garbage collection on or OFF TiDBGCEnable = "tidb_gc_enable" // TiDBGCRunInterval sets the interval that GC runs @@ -879,6 +883,8 @@ const ( DefTiDBStatsCacheMemQuota = 0 MaxTiDBStatsCacheMemQuota = 1024 * 1024 * 1024 * 1024 // 1TB DefTiDBQueryLogMaxLen = 4096 + DefTiDBTxnTotalSizeLimit = 100 * 1024 * 1024 + DefTiDBTxnEntrySizeLimit = 6 * 1024 * 1024 DefRequireSecureTransport = false DefTiDBCommitterConcurrency = 128 DefTiDBBatchDMLIgnoreError = false @@ -926,6 +932,8 @@ var ( StatsLoadPseudoTimeout = atomic.NewBool(DefTiDBStatsLoadPseudoTimeout) MemQuotaBindingCache = atomic.NewInt64(DefTiDBMemQuotaBindingCache) GCMaxWaitTime = atomic.NewInt64(DefTiDBGCMaxWaitTime) + TxnTotalSizeLimit uint64 = DefTiDBTxnTotalSizeLimit + TxnEntrySizeLimit uint64 = DefTiDBTxnEntrySizeLimit StatsCacheMemQuota = atomic.NewInt64(DefTiDBStatsCacheMemQuota) OOMAction = atomic.NewString(DefTiDBMemOOMAction) // variables for plan cache diff --git a/store/driver/txn/txn_driver.go b/store/driver/txn/txn_driver.go index 196db69f6e419..ace6cca0e6a46 100644 --- a/store/driver/txn/txn_driver.go +++ b/store/driver/txn/txn_driver.go @@ -16,7 +16,6 @@ package txn import ( "context" - "sync/atomic" "github.com/opentracing/opentracing-go" "github.com/pingcap/errors" @@ -48,8 +47,8 @@ type tikvTxn struct { func NewTiKVTxn(txn *tikv.KVTxn) kv.Transaction { txn.SetKVFilter(TiDBKVFilter{}) - entryLimit := atomic.LoadUint64(&kv.TxnEntrySizeLimit) - totalLimit := atomic.LoadUint64(&kv.TxnTotalSizeLimit) + entryLimit := kv.TxnEntrySizeLimit.Load() + totalLimit := kv.TxnTotalSizeLimit.Load() txn.GetUnionStore().SetEntrySizeLimit(entryLimit, totalLimit) return &tikvTxn{txn, make(map[int64]*model.TableInfo), nil} diff --git a/tidb-server/main.go b/tidb-server/main.go index a211360d6ec2b..7fe7ff080bc6b 100644 --- a/tidb-server/main.go +++ b/tidb-server/main.go @@ -609,12 +609,6 @@ func setGlobalVars() { } plannercore.AllowCartesianProduct.Store(cfg.Performance.CrossJoin) privileges.SkipWithGrant = cfg.Security.SkipGrantTable - kv.TxnTotalSizeLimit = cfg.Performance.TxnTotalSizeLimit - if cfg.Performance.TxnEntrySizeLimit > 120*1024*1024 { - log.Fatal("cannot set txn entry size limit larger than 120M") - } - kv.TxnEntrySizeLimit = cfg.Performance.TxnEntrySizeLimit - priority := mysql.Str2Priority(cfg.Instance.ForcePriority) variable.ForcePriority = int32(priority)