Skip to content

Commit 296420e

Browse files
authored
*: use clustered PRIMARY KEY for stats system tables (#68324)
close #66751
1 parent 2b9527b commit 296420e

7 files changed

Lines changed: 88 additions & 28 deletions

File tree

pkg/executor/hot_regions_history_table_test.go

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -187,9 +187,6 @@ func TestTiDBHotRegionsHistory(t *testing.T) {
187187
// mysql table_id = 21 ,index_id = 1, table_name = STATS_META, index_name = IDX_VER
188188
{"2019-10-10 10:10:19", "MYSQL", "STATS_META", statsMetaTidStr, "IDX_VER", "1", "3", "3", "33333", "0", "1", "READ", "99", "99", "99", "99"},
189189
{"2019-10-10 10:10:20", "MYSQL", "STATS_META", statsMetaTidStr, "IDX_VER", "1", "4", "4", "44444", "0", "0", "WRITE", "99", "99", "99", "99"},
190-
// mysql table_id = 21 ,index_id = 2, table_name = STATS_META, index_name = TBL
191-
{"2019-10-10 10:10:21", "MYSQL", "STATS_META", statsMetaTidStr, "TBL", "2", "5", "5", "55555", "0", "1", "READ", "99", "99", "99", "99"},
192-
{"2019-10-10 10:10:22", "MYSQL", "STATS_META", statsMetaTidStr, "TBL", "2", "6", "6", "66666", "0", "0", "WRITE", "99", "99", "99", "99"},
193190
// table_id = 1313, index_id = 1, deleted schema
194191
{"2019-10-10 10:10:23", "UNKNOWN", "UNKNOWN", "1313", "UNKNOWN", "1", "7", "7", "77777", "0", "1", "READ", "99", "99", "99", "99"},
195192
{"2019-10-10 10:10:24", "UNKNOWN", "UNKNOWN", "1313", "UNKNOWN", "1", "8", "8", "88888", "0", "0", "WRITE", "99", "99", "99", "99"},
@@ -275,12 +272,6 @@ func TestTiDBHotRegionsHistory(t *testing.T) {
275272
StartKey: helper.NewIndexWithKeyRange(mockDB, &model.TableInfo{ID: statsMetaTid}, &model.IndexInfo{ID: 1}, storeCodec).StartKey,
276273
EndKey: helper.NewIndexWithKeyRange(mockDB, &model.TableInfo{ID: statsMetaTid}, &model.IndexInfo{ID: 1}, storeCodec).EndKey,
277274
},
278-
// mysql table_id = 21 ,index_id = 2, table_name = STATS_META, index_name = TBL
279-
{UpdateTime: unixTimeMs("2019-10-10 10:10:21"), RegionID: 5, StoreID: 5, PeerID: 55555, IsLearner: false,
280-
IsLeader: true, HotRegionType: "READ", HotDegree: 99, FlowBytes: 99, KeyRate: 99, QueryRate: 99,
281-
StartKey: helper.NewIndexWithKeyRange(mockDB, &model.TableInfo{ID: statsMetaTid}, &model.IndexInfo{ID: 2}, storeCodec).StartKey,
282-
EndKey: helper.NewIndexWithKeyRange(mockDB, &model.TableInfo{ID: statsMetaTid}, &model.IndexInfo{ID: 2}, storeCodec).EndKey,
283-
},
284275
// table_id = 1313, index_id = 1, deleted schema
285276
{UpdateTime: unixTimeMs("2019-10-10 10:10:23"), RegionID: 7, StoreID: 7, PeerID: 77777, IsLeader: true,
286277
HotRegionType: "READ", HotDegree: 99, FlowBytes: 99, KeyRate: 99, QueryRate: 99,
@@ -297,12 +288,6 @@ func TestTiDBHotRegionsHistory(t *testing.T) {
297288
StartKey: helper.NewIndexWithKeyRange(mockDB, &model.TableInfo{ID: statsMetaTid}, &model.IndexInfo{ID: 1}, storeCodec).StartKey,
298289
EndKey: helper.NewIndexWithKeyRange(mockDB, &model.TableInfo{ID: statsMetaTid}, &model.IndexInfo{ID: 1}, storeCodec).EndKey,
299290
},
300-
// mysql table_id = 21 ,index_id = 2, table_name = STATS_META, index_name = TBL
301-
{UpdateTime: unixTimeMs("2019-10-10 10:10:22"), RegionID: 6, StoreID: 6, PeerID: 66666, IsLearner: false,
302-
IsLeader: false, HotRegionType: "WRITE", HotDegree: 99, FlowBytes: 99, KeyRate: 99, QueryRate: 99,
303-
StartKey: helper.NewIndexWithKeyRange(mockDB, &model.TableInfo{ID: statsMetaTid}, &model.IndexInfo{ID: 2}, storeCodec).StartKey,
304-
EndKey: helper.NewIndexWithKeyRange(mockDB, &model.TableInfo{ID: statsMetaTid}, &model.IndexInfo{ID: 2}, storeCodec).EndKey,
305-
},
306291
// table_id = 1313, index_id = 1, deleted schema
307292
{UpdateTime: unixTimeMs("2019-10-10 10:10:24"), RegionID: 8, StoreID: 8, PeerID: 88888, IsLearner: false,
308293
IsLeader: false, HotRegionType: "WRITE", HotDegree: 99, FlowBytes: 99, KeyRate: 99, QueryRate: 99,
@@ -328,7 +313,7 @@ func TestTiDBHotRegionsHistory(t *testing.T) {
328313
fullHotRegions[0], fullHotRegions[1], fullHotRegions[2],
329314
fullHotRegions[3],
330315
fullHotRegions[6], fullHotRegions[7], fullHotRegions[8],
331-
fullHotRegions[9], fullHotRegions[10], fullHotRegions[11],
316+
fullHotRegions[9],
332317
},
333318
},
334319
{
@@ -340,7 +325,7 @@ func TestTiDBHotRegionsHistory(t *testing.T) {
340325
fullHotRegions[0], fullHotRegions[1], fullHotRegions[2],
341326
fullHotRegions[3],
342327
fullHotRegions[6], fullHotRegions[7], fullHotRegions[8],
343-
fullHotRegions[9], fullHotRegions[10], fullHotRegions[11],
328+
fullHotRegions[9],
344329
},
345330
},
346331
{

pkg/meta/metadef/system_tables_def.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -173,8 +173,8 @@ const (
173173
count BIGINT(64) UNSIGNED NOT NULL DEFAULT 0,
174174
snapshot BIGINT(64) UNSIGNED NOT NULL DEFAULT 0,
175175
last_stats_histograms_version BIGINT(64) UNSIGNED DEFAULT NULL,
176-
INDEX idx_ver(version),
177-
UNIQUE INDEX tbl(table_id)
176+
PRIMARY KEY (table_id) CLUSTERED,
177+
INDEX idx_ver(version)
178178
);`
179179

180180
// CreateStatsHistogramsTable stores the statistics of table columns.
@@ -192,7 +192,7 @@ const (
192192
flag BIGINT(64) NOT NULL DEFAULT 0,
193193
correlation DOUBLE NOT NULL DEFAULT 0,
194194
last_analyze_pos LONGBLOB DEFAULT NULL,
195-
UNIQUE INDEX tbl(table_id, is_index, hist_id)
195+
PRIMARY KEY (table_id, is_index, hist_id) CLUSTERED
196196
);`
197197

198198
// CreateStatsBucketsTable stores the histogram info for every table columns.
@@ -206,7 +206,7 @@ const (
206206
upper_bound LONGBLOB NOT NULL,
207207
lower_bound LONGBLOB ,
208208
ndv BIGINT NOT NULL DEFAULT 0,
209-
UNIQUE INDEX tbl(table_id, is_index, hist_id, bucket_id)
209+
PRIMARY KEY (table_id, is_index, hist_id, bucket_id) CLUSTERED
210210
);`
211211

212212
// CreateGCDeleteRangeTable stores schemas which can be deleted by DeleteRange.
@@ -293,7 +293,7 @@ const (
293293
is_index TINYINT(2) NOT NULL,
294294
hist_id BIGINT(64) NOT NULL,
295295
value LONGBLOB,
296-
INDEX tbl(table_id, is_index, hist_id)
296+
PRIMARY KEY (table_id, is_index, hist_id) CLUSTERED
297297
);`
298298

299299
// CreateExprPushdownBlacklistTable stores the expressions which are not allowed to be pushed down.
@@ -382,7 +382,7 @@ const (
382382
seq_no bigint(64) NOT NULL comment 'sequence number of the gzipped data slice',
383383
version bigint(64) NOT NULL comment 'stats version which corresponding to stats:version in EXPLAIN',
384384
create_time datetime(6) NOT NULL,
385-
UNIQUE KEY table_version_seq (table_id, version, seq_no),
385+
PRIMARY KEY (table_id, version, seq_no) CLUSTERED,
386386
KEY table_create_time (table_id, create_time, seq_no),
387387
KEY idx_create_time (create_time)
388388
);`
@@ -394,7 +394,7 @@ const (
394394
version bigint(64) NOT NULL comment 'stats version which corresponding to stats:version in EXPLAIN',
395395
source varchar(40) NOT NULL,
396396
create_time datetime(6) NOT NULL,
397-
UNIQUE KEY table_version (table_id, version),
397+
PRIMARY KEY (table_id, version) CLUSTERED,
398398
KEY table_create_time (table_id, create_time),
399399
KEY idx_create_time (create_time)
400400
);`
@@ -461,7 +461,7 @@ const (
461461
modify_count bigint(64) NOT NULL DEFAULT 0,
462462
count bigint(64) NOT NULL DEFAULT 0,
463463
version bigint(64) UNSIGNED NOT NULL DEFAULT 0,
464-
PRIMARY KEY (table_id));`
464+
PRIMARY KEY (table_id) CLUSTERED);`
465465

466466
// CreatePasswordHistoryTable is a table save history passwd.
467467
CreatePasswordHistoryTable = `CREATE TABLE IF NOT EXISTS mysql.password_history (

pkg/session/test/bootstraptest/BUILD.bazel

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ go_test(
99
"main_test.go",
1010
],
1111
flaky = True,
12-
shard_count = 43,
12+
shard_count = 44,
1313
deps = [
1414
"//pkg/config",
1515
"//pkg/config/kerneltype",

pkg/session/test/bootstraptest/bootstrap_upgrade_test.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,71 @@ func TestUpgradeVersion83AndVersion84(t *testing.T) {
102102
}
103103
}
104104

105+
// TestMysqlTablesWithoutClusteredPK pins the `mysql` base tables without a clustered
106+
// PRIMARY KEY. A diff flags a regression or a new table whose PK type was not
107+
// considered; the list also serves as a reference for future clustered-PK work.
108+
//
109+
// The classic and next-gen kernels bootstrap system tables through different paths
110+
// (DDL statements vs. direct meta KV writes). The next-gen path clusters every table
111+
// that has a PRIMARY KEY, while the classic path leaves composite and non-integer PKs
112+
// non-clustered, so the expected set differs by kernel.
113+
func TestMysqlTablesWithoutClusteredPK(t *testing.T) {
114+
store := testkit.CreateMockStore(t)
115+
tk := testkit.NewTestKit(t, store)
116+
117+
expected := []string{
118+
"advisory_locks",
119+
"bind_info",
120+
"columns_priv",
121+
"db",
122+
"default_roles",
123+
"dist_framework_meta",
124+
"expr_pushdown_blacklist",
125+
"gc_delete_range",
126+
"gc_delete_range_done",
127+
"global_grants",
128+
"global_priv",
129+
"global_variables",
130+
"opt_rule_blacklist",
131+
"password_history",
132+
"plan_replayer_status",
133+
"plan_replayer_task",
134+
"request_unit_by_group",
135+
"role_edges",
136+
"stats_extended",
137+
"stats_feedback",
138+
"stats_top_n",
139+
"tables_priv",
140+
"tidb",
141+
"tidb_ddl_reorg",
142+
"tidb_kernel_options",
143+
"tidb_pitr_id_map",
144+
"tidb_runaway_queries",
145+
"tidb_ttl_job_history",
146+
"tidb_ttl_task",
147+
"user",
148+
}
149+
if kerneltype.IsNextGen() {
150+
expected = []string{
151+
"bind_info",
152+
"expr_pushdown_blacklist",
153+
"gc_delete_range",
154+
"gc_delete_range_done",
155+
"opt_rule_blacklist",
156+
"plan_replayer_status",
157+
"stats_feedback",
158+
"stats_top_n",
159+
"tidb_ddl_reorg",
160+
"tidb_runaway_queries",
161+
}
162+
}
163+
tk.MustQuery(
164+
"SELECT table_name FROM information_schema.tables " +
165+
"WHERE table_schema = 'mysql' AND table_type = 'BASE TABLE' " +
166+
"AND tidb_pk_type <> 'CLUSTERED' ORDER BY table_name",
167+
).Check(testkit.Rows(expected...))
168+
}
169+
105170
func revertVersionAndVariables(t *testing.T, se sessionapi.Session, ver int) {
106171
session.MustExec(t, se, fmt.Sprintf("update mysql.tidb set variable_value='%d' where variable_name='tidb_server_version'", ver))
107172
if ver <= 195 {

pkg/statistics/handle/bootstrap.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,10 @@ func (o genHistSQLOptions) assert() {
352352
// We need to read all the records since we need to do initialization of table.ColAndIdxExistenceMap.
353353
func genInitStatsHistogramsSQL(options genHistSQLOptions) string {
354354
options.assert()
355+
// Keep the ORDER_INDEX(tbl) hint: `tbl` still exists on clusters bootstrapped
356+
// before stats_histograms moved to a clustered PRIMARY KEY. On fresh clusters
357+
// it is inapplicable but harmless: the clustered PK scan is already ordered by
358+
// table_id.
355359
selectPrefix := "select /*+ ORDER_INDEX(mysql.stats_histograms,tbl) */ HIGH_PRIORITY table_id, is_index, hist_id, distinct_count, version, null_count, cm_sketch, tot_col_size, stats_ver, correlation from mysql.stats_histograms"
356360
orderSuffix := " order by table_id"
357361

@@ -588,6 +592,7 @@ func genInitStatsTopNSQLForIndexes(isPaging bool, tableRange [2]int64) string {
588592
// Returns a map where keys are table IDs that have bucket entries.
589593
func getTablesWithBucketsInRange(sctx sessionctx.Context, tableRange [2]int64) (map[int64]struct{}, error) {
590594
// Query to find table_ids that have buckets in the given range
595+
// Keep the USE_INDEX(tbl) hint for upgraded clusters; see genInitStatsHistogramsSQL.
591596
// TODO: Figure out if HIGH_PRIORITY is working as intended here.
592597
sql := "select /*+ USE_INDEX(stats_buckets, tbl) */ HIGH_PRIORITY distinct table_id from mysql.stats_buckets" +
593598
" where is_index = 1" +
@@ -732,6 +737,7 @@ func (*Handle) initStatsBuckets4Chunk(cache statstypes.StatsCache, iter *chunk.I
732737
// We only need to load the indexes' since we only record the existence of columns in ColAndIdxExistenceMap.
733738
// The stats of the column is not loaded during the bootstrap process.
734739
func genInitStatsBucketsSQLForIndexes(isPaging bool, tableRange [2]int64) string {
740+
// Keep the ORDER_INDEX(tbl) hint for upgraded clusters; see genInitStatsHistogramsSQL.
735741
selectPrefix := "select /*+ ORDER_INDEX(mysql.stats_buckets,tbl) */ HIGH_PRIORITY table_id, hist_id, count, repeats, lower_bound, upper_bound, ndv from mysql.stats_buckets where is_index=1"
736742
orderSuffix := " order by table_id"
737743
if !isPaging {

pkg/statistics/handle/history/history_stats.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,11 @@ func RecordHistoricalStatsMeta(
158158
}
159159

160160
modifyCount, count := rows[0].GetInt64(0), rows[0].GetInt64(1)
161-
const sql = "REPLACE INTO mysql.stats_meta_history(table_id, modify_count, count, version, source, create_time) VALUES (%?, %?, %?, %?, %?, NOW())"
161+
// Use NOW(6) to match the datetime(6) precision of the create_time column.
162+
// Plain NOW() truncates to seconds, so rows written within the same second
163+
// share an indistinguishable create_time. In-tree readers key off `version`,
164+
// but full precision keeps create_time usable for ordering and auditing.
165+
const sql = "REPLACE INTO mysql.stats_meta_history(table_id, modify_count, count, version, source, create_time) VALUES (%?, %?, %?, %?, %?, NOW(6))"
162166
if _, err := handleutil.ExecWithCtx(
163167
ctx,
164168
sctx,

tests/integrationtest/r/executor/infoschema_reader.result

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ set global tidb_ddl_enable_fast_reorg = default;
179179
set global tidb_enable_dist_task = default;
180180
select * from information_schema.KEY_COLUMN_USAGE where TABLE_NAME='stats_meta' and COLUMN_NAME='table_id';
181181
CONSTRAINT_CATALOG CONSTRAINT_SCHEMA CONSTRAINT_NAME TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME ORDINAL_POSITION POSITION_IN_UNIQUE_CONSTRAINT REFERENCED_TABLE_SCHEMA REFERENCED_TABLE_NAME REFERENCED_COLUMN_NAME
182-
def mysql tbl def mysql stats_meta table_id 1 NULL NULL NULL NULL
182+
def mysql PRIMARY def mysql stats_meta table_id 1 1 NULL NULL NULL
183183
create user key_column_tester;
184184
select * from information_schema.KEY_COLUMN_USAGE where TABLE_NAME != 'CLUSTER_SLOW_QUERY';
185185
CONSTRAINT_CATALOG CONSTRAINT_SCHEMA CONSTRAINT_NAME TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME ORDINAL_POSITION POSITION_IN_UNIQUE_CONSTRAINT REFERENCED_TABLE_SCHEMA REFERENCED_TABLE_NAME REFERENCED_COLUMN_NAME

0 commit comments

Comments
 (0)