@@ -19,6 +19,7 @@ import (
19
19
"encoding/json"
20
20
"fmt"
21
21
"slices"
22
+ "strings"
22
23
23
24
"github.com/pingcap/errors"
24
25
"github.com/pingcap/tidb/pkg/parser/ast"
@@ -55,36 +56,90 @@ func UpdateStatsVersion(ctx context.Context, sctx sessionctx.Context) error {
55
56
return nil
56
57
}
57
58
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.
59
77
func UpdateStatsMeta (
60
78
ctx context.Context ,
61
79
sctx sessionctx.Context ,
62
80
startTS uint64 ,
63
- delta variable.TableDelta ,
64
- id int64 ,
65
- isLocked bool ,
81
+ updates ... * DeltaUpdate ,
66
82
) (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 ) )
79
95
} 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
84
124
}
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 {
85
139
cache .TableRowStatsCache .Invalidate (id )
86
140
}
87
- return err
141
+
142
+ return nil
88
143
}
89
144
90
145
// InsertExtendedStats inserts a record into mysql.stats_extended and update version in mysql.stats_meta.
0 commit comments