Skip to content

Commit 67249cb

Browse files
authored
statistics: refactor stats meta handling to use DeltaUpdate for multi-table support (#58657)
ref #57869
1 parent b2f2faa commit 67249cb

File tree

3 files changed

+96
-32
lines changed

3 files changed

+96
-32
lines changed

pkg/statistics/handle/ddl/subscriber.go

+2-6
Original file line numberDiff line numberDiff line change
@@ -413,9 +413,7 @@ func updateGlobalTableStats4DropPartition(
413413
ctx,
414414
sctx,
415415
startTS,
416-
variable.TableDelta{Count: count, Delta: delta},
417-
globalTableInfo.ID,
418-
isLocked,
416+
storage.NewDeltaUpdate(globalTableInfo.ID, variable.TableDelta{Count: count, Delta: delta}, isLocked),
419417
))
420418
}
421419

@@ -597,9 +595,7 @@ func updateGlobalTableStats4TruncatePartition(
597595
ctx,
598596
sctx,
599597
startTS,
600-
variable.TableDelta{Count: count, Delta: delta},
601-
globalTableInfo.ID,
602-
isLocked,
598+
storage.NewDeltaUpdate(globalTableInfo.ID, variable.TableDelta{Count: count, Delta: delta}, isLocked),
603599
)
604600
if err != nil {
605601
fields := truncatePartitionsLogFields(

pkg/statistics/handle/storage/update.go

+76-21
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"encoding/json"
2020
"fmt"
2121
"slices"
22+
"strings"
2223

2324
"github.com/pingcap/errors"
2425
"github.com/pingcap/tidb/pkg/parser/ast"
@@ -55,36 +56,90 @@ func UpdateStatsVersion(ctx context.Context, sctx sessionctx.Context) error {
5556
return nil
5657
}
5758

58-
// UpdateStatsMeta update the stats meta stat for this Table.
59+
// DeltaUpdate is the delta update for stats meta.
60+
type DeltaUpdate struct {
61+
Delta variable.TableDelta
62+
TableID int64
63+
IsLocked bool
64+
}
65+
66+
// NewDeltaUpdate creates a new DeltaUpdate.
67+
func NewDeltaUpdate(tableID int64, delta variable.TableDelta, isLocked bool) *DeltaUpdate {
68+
return &DeltaUpdate{
69+
Delta: delta,
70+
TableID: tableID,
71+
IsLocked: isLocked,
72+
}
73+
}
74+
75+
// UpdateStatsMeta updates the stats meta for multiple tables.
76+
// It uses the INSERT INTO ... ON DUPLICATE KEY UPDATE syntax to fill the missing records.
5977
func UpdateStatsMeta(
6078
ctx context.Context,
6179
sctx sessionctx.Context,
6280
startTS uint64,
63-
delta variable.TableDelta,
64-
id int64,
65-
isLocked bool,
81+
updates ...*DeltaUpdate,
6682
) (err error) {
67-
if isLocked {
68-
// use INSERT INTO ... ON DUPLICATE KEY UPDATE here to fill missing stats_table_locked.
69-
// Note: For locked tables, it is possible that the record gets deleted. So it can be negative.
70-
_, err = statsutil.ExecWithCtx(ctx, sctx, "insert into mysql.stats_table_locked (version, table_id, modify_count, count) values (%?, %?, %?, %?) on duplicate key "+
71-
"update version = values(version), modify_count = modify_count + values(modify_count), count = count + values(count)",
72-
startTS, id, delta.Count, delta.Delta)
73-
} else {
74-
if delta.Delta < 0 {
75-
// use INSERT INTO ... ON DUPLICATE KEY UPDATE here to fill missing stats_meta.
76-
_, err = statsutil.ExecWithCtx(ctx, sctx, "insert into mysql.stats_meta (version, table_id, modify_count, count) values (%?, %?, %?, 0) on duplicate key "+
77-
"update version = values(version), modify_count = modify_count + values(modify_count), count = if(count > %?, count - %?, 0)",
78-
startTS, id, delta.Count, -delta.Delta, -delta.Delta)
83+
if len(updates) == 0 {
84+
return nil
85+
}
86+
87+
// Separate locked and unlocked updates
88+
var lockedValues, unlockedPosValues, unlockedNegValues []string
89+
var cacheInvalidateIDs []int64
90+
91+
for _, update := range updates {
92+
if update.IsLocked {
93+
lockedValues = append(lockedValues, fmt.Sprintf("(%d, %d, %d, %d)",
94+
startTS, update.TableID, update.Delta.Count, update.Delta.Delta))
7995
} else {
80-
// use INSERT INTO ... ON DUPLICATE KEY UPDATE here to fill missing stats_meta.
81-
_, err = statsutil.ExecWithCtx(ctx, sctx, "insert into mysql.stats_meta (version, table_id, modify_count, count) values (%?, %?, %?, %?) on duplicate key "+
82-
"update version = values(version), modify_count = modify_count + values(modify_count), count = count + values(count)", startTS,
83-
id, delta.Count, delta.Delta)
96+
if update.Delta.Delta < 0 {
97+
unlockedNegValues = append(unlockedNegValues, fmt.Sprintf("(%d, %d, %d, %d)",
98+
startTS, update.TableID, update.Delta.Count, -update.Delta.Delta))
99+
} else {
100+
unlockedPosValues = append(unlockedPosValues, fmt.Sprintf("(%d, %d, %d, %d)",
101+
startTS, update.TableID, update.Delta.Count, update.Delta.Delta))
102+
}
103+
cacheInvalidateIDs = append(cacheInvalidateIDs, update.TableID)
104+
}
105+
}
106+
107+
// Execute locked updates
108+
if len(lockedValues) > 0 {
109+
sql := fmt.Sprintf("insert into mysql.stats_table_locked (version, table_id, modify_count, count) values %s "+
110+
"on duplicate key update version = values(version), modify_count = modify_count + values(modify_count), "+
111+
"count = count + values(count)", strings.Join(lockedValues, ","))
112+
if _, err = statsutil.ExecWithCtx(ctx, sctx, sql); err != nil {
113+
return err
114+
}
115+
}
116+
117+
// Execute unlocked updates with positive delta
118+
if len(unlockedPosValues) > 0 {
119+
sql := fmt.Sprintf("insert into mysql.stats_meta (version, table_id, modify_count, count) values %s "+
120+
"on duplicate key update version = values(version), modify_count = modify_count + values(modify_count), "+
121+
"count = count + values(count)", strings.Join(unlockedPosValues, ","))
122+
if _, err = statsutil.ExecWithCtx(ctx, sctx, sql); err != nil {
123+
return err
84124
}
125+
}
126+
127+
// Execute unlocked updates with negative delta
128+
if len(unlockedNegValues) > 0 {
129+
sql := fmt.Sprintf("insert into mysql.stats_meta (version, table_id, modify_count, count) values %s "+
130+
"on duplicate key update version = values(version), modify_count = modify_count + values(modify_count), "+
131+
"count = if(count > values(count), count - values(count), 0)", strings.Join(unlockedNegValues, ","))
132+
if _, err = statsutil.ExecWithCtx(ctx, sctx, sql); err != nil {
133+
return err
134+
}
135+
}
136+
137+
// Invalidate cache for all unlocked tables
138+
for _, id := range cacheInvalidateIDs {
85139
cache.TableRowStatsCache.Invalidate(id)
86140
}
87-
return err
141+
142+
return nil
88143
}
89144

90145
// InsertExtendedStats inserts a record into mysql.stats_extended and update version in mysql.stats_meta.

pkg/statistics/handle/usage/session_stats_collect.go

+18-5
Original file line numberDiff line numberDiff line change
@@ -172,8 +172,12 @@ func (s *statsUsageImpl) dumpTableStatCountToKV(is infoschema.InfoSchema, physic
172172
}
173173
tableOrPartitionLocked := isTableLocked || isPartitionLocked
174174
isLocked = tableOrPartitionLocked
175-
if err = storage.UpdateStatsMeta(utilstats.StatsCtx, sctx, statsVersion, delta,
176-
physicalTableID, tableOrPartitionLocked); err != nil {
175+
if err = storage.UpdateStatsMeta(
176+
utilstats.StatsCtx,
177+
sctx,
178+
statsVersion,
179+
storage.NewDeltaUpdate(physicalTableID, delta, tableOrPartitionLocked),
180+
); err != nil {
177181
return err
178182
}
179183
affectedRows += sctx.GetSessionVars().StmtCtx.AffectedRows()
@@ -190,7 +194,12 @@ func (s *statsUsageImpl) dumpTableStatCountToKV(is infoschema.InfoSchema, physic
190194
// To sum up, we only need to update the global-stats when the table and the partition are not locked.
191195
if !isTableLocked && !isPartitionLocked {
192196
// If it's a partitioned table and its global-stats exists, update its count and modify_count as well.
193-
if err = storage.UpdateStatsMeta(utilstats.StatsCtx, sctx, statsVersion, delta, tableID, isTableLocked); err != nil {
197+
if err = storage.UpdateStatsMeta(
198+
utilstats.StatsCtx,
199+
sctx,
200+
statsVersion,
201+
storage.NewDeltaUpdate(tableID, delta, isTableLocked),
202+
); err != nil {
194203
return err
195204
}
196205
affectedRows += sctx.GetSessionVars().StmtCtx.AffectedRows()
@@ -203,8 +212,12 @@ func (s *statsUsageImpl) dumpTableStatCountToKV(is infoschema.InfoSchema, physic
203212
isTableLocked = true
204213
}
205214
isLocked = isTableLocked
206-
if err = storage.UpdateStatsMeta(utilstats.StatsCtx, sctx, statsVersion, delta,
207-
physicalTableID, isTableLocked); err != nil {
215+
if err = storage.UpdateStatsMeta(
216+
utilstats.StatsCtx,
217+
sctx,
218+
statsVersion,
219+
storage.NewDeltaUpdate(physicalTableID, delta, isTableLocked),
220+
); err != nil {
208221
return err
209222
}
210223
affectedRows += sctx.GetSessionVars().StmtCtx.AffectedRows()

0 commit comments

Comments
 (0)