Skip to content

Commit 1d2fde1

Browse files
committed
fix
Signed-off-by: hi-rustin <[email protected]>
1 parent b75a314 commit 1d2fde1

File tree

4 files changed

+93
-7
lines changed

4 files changed

+93
-7
lines changed

statistics/handle/handle.go

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1086,8 +1086,8 @@ func (h *Handle) loadNeededColumnHistograms(reader *statistics.StatsReader, col
10861086
return errors.Trace(err)
10871087
}
10881088
if len(rows) == 0 {
1089-
logutil.BgLogger().Error("fail to get stats version for this histogram", zap.Int64("table_id", col.TableID), zap.Int64("hist_id", col.ID))
1090-
return errors.Trace(fmt.Errorf("fail to get stats version for this histogram, table_id:%v, hist_id:%v", col.TableID, col.ID))
1089+
logutil.BgLogger().Error("fail to get stats version for this histogram, normally this wouldn't happen, please check if this column or index has a histogram record in `mysql.stats_histogram`", zap.Int64("table_id", col.TableID), zap.Int64("column_id", col.ID))
1090+
return errors.Trace(fmt.Errorf("fail to get stats version for this histogram, normally this wouldn't happen, please check if this column or index has a histogram record in `mysql.stats_histogram`, table_id:%v, column_id:%v", col.TableID, col.ID))
10911091
}
10921092
statsVer := rows[0].GetInt64(0)
10931093
colHist := &statistics.Column{
@@ -1125,7 +1125,16 @@ func (h *Handle) loadNeededIndexHistograms(reader *statistics.StatsReader, idx m
11251125
return nil
11261126
}
11271127
index, ok := tbl.Indices[idx.ID]
1128-
if !ok {
1128+
// Double check if the index is really needed to load.
1129+
// If we don't do this it might cause a memory leak.
1130+
// See: https://github.com/pingcap/tidb/issues/54022
1131+
if !ok || !index.IsLoadNeeded() {
1132+
if !index.IsLoadNeeded() {
1133+
logutil.BgLogger().Warn(
1134+
"Although the index stats is not required to load, an attempt is still made to load it, skip it",
1135+
zap.Int64("table_id", idx.TableID), zap.Int64("hist_id", idx.ID),
1136+
)
1137+
}
11291138
statistics.HistogramNeededItems.Delete(idx)
11301139
return nil
11311140
}
@@ -1149,8 +1158,8 @@ func (h *Handle) loadNeededIndexHistograms(reader *statistics.StatsReader, idx m
11491158
return errors.Trace(err)
11501159
}
11511160
if len(rows) == 0 {
1152-
logutil.BgLogger().Error("fail to get stats version for this histogram", zap.Int64("table_id", idx.TableID), zap.Int64("hist_id", idx.ID))
1153-
return errors.Trace(fmt.Errorf("fail to get stats version for this histogram, table_id:%v, hist_id:%v", idx.TableID, idx.ID))
1161+
logutil.BgLogger().Error("fail to get stats version for this histogram, normally this wouldn't happen, please check if this column or index has a histogram record in `mysql.stats_histogram`", zap.Int64("table_id", idx.TableID), zap.Int64("index_id", idx.ID))
1162+
return errors.Trace(fmt.Errorf("fail to get stats version for this histogram, normally this wouldn't happen, please check if this column or index has a histogram record in `mysql.stats_histogram`, table_id:%v, index_id:%v", idx.TableID, idx.ID))
11541163
}
11551164
idxHist := &statistics.Index{Histogram: *hg, CMSketch: cms, TopN: topN, FMSketch: fms,
11561165
Info: index.Info, ErrorRate: index.ErrorRate, StatsVer: rows[0].GetInt64(0),

statistics/handle/handle_hist.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -366,9 +366,9 @@ func (h *Handle) readStatsForOneItem(item model.TableItemID, w *statsWrapper, re
366366
return nil, errors.Trace(err)
367367
}
368368
if len(rows) == 0 {
369-
logutil.BgLogger().Error("fail to get stats version for this histogram", zap.Int64("table_id", item.TableID),
369+
logutil.BgLogger().Error("fail to get stats version for this histogram, normally this wouldn't happen, please check if this column or index has a histogram record in `mysql.stats_histogram`", zap.Int64("table_id", item.TableID),
370370
zap.Int64("hist_id", item.ID), zap.Bool("is_index", item.IsIndex))
371-
return nil, errors.Trace(fmt.Errorf("fail to get stats version for this histogram, table_id:%v, hist_id:%v, is_index:%v", item.TableID, item.ID, item.IsIndex))
371+
return nil, errors.Trace(fmt.Errorf("fail to get stats version for this histogram, normally this wouldn't happen, please check if this column or index has a histogram record in `mysql.stats_histogram`, table_id:%v, hist_id:%v, is_index:%v", item.TableID, item.ID, item.IsIndex))
372372
}
373373
statsVer := rows[0].GetInt64(0)
374374
if item.IsIndex {

tests/realtikvtest/statisticstest/BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ go_test(
1010
flaky = True,
1111
race = "on",
1212
deps = [
13+
"//parser/model",
14+
"//statistics",
1315
"//statistics/handle",
1416
"//testkit",
1517
"//tests/realtikvtest",

tests/realtikvtest/statisticstest/statistics_test.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,13 @@
1515
package statisticstest
1616

1717
import (
18+
"context"
1819
"fmt"
1920
"testing"
21+
"time"
2022

23+
"github.com/pingcap/tidb/parser/model"
24+
"github.com/pingcap/tidb/statistics"
2125
"github.com/pingcap/tidb/statistics/handle"
2226
"github.com/pingcap/tidb/testkit"
2327
"github.com/pingcap/tidb/tests/realtikvtest"
@@ -186,3 +190,74 @@ func TestNewCollationStatsWithPrefixIndex(t *testing.T) {
186190
"1 3 15 0 2 0",
187191
))
188192
}
193+
194+
func TestNoNeedIndexStatsLoading(t *testing.T) {
195+
store, dom := realtikvtest.CreateMockStoreAndDomainAndSetup(t)
196+
tk := testkit.NewTestKit(t, store)
197+
tk.MustExec("use test;")
198+
tk.MustExec("drop table if exists t;")
199+
// 1. Create a table and the statsHandle.Update(do.InfoSchema()) will load this table into the stats cache.
200+
tk.MustExec("create table if not exists t(a int, b int, index ia(a));")
201+
// 2. Drop the stats of the stats, it will clean up all system table records for this table.
202+
tk.MustExec("drop stats t;")
203+
// 3. Insert some data and wait for the modify_count and the count is not null in the mysql.stats_meta.
204+
tk.MustExec("insert into t value(1,1), (2,2);")
205+
h := dom.StatsHandle()
206+
require.NoError(t, h.DumpStatsDeltaToKV(true))
207+
require.Eventually(t, func() bool {
208+
rows := tk.MustQuery("show stats_meta").Rows()
209+
return len(rows) > 0
210+
}, 1*time.Minute, 2*time.Millisecond)
211+
require.NoError(t, h.Update(dom.InfoSchema()))
212+
// 4. Try to select some data from this table by ID, it would trigger an async load.
213+
tk.MustExec("set tidb_opt_objective='determinate';")
214+
tk.MustQuery("select * from t where a = 1 and b = 1;").Check(testkit.Rows("1 1"))
215+
table, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t"))
216+
require.NoError(t, err)
217+
checkTableIDInItems(t, table.Meta().ID)
218+
}
219+
220+
func checkTableIDInItems(t *testing.T, tableID int64) {
221+
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)
222+
defer cancel()
223+
224+
ticker := time.NewTicker(2 * time.Millisecond)
225+
defer ticker.Stop()
226+
227+
done := make(chan bool)
228+
229+
// First, confirm that the table ID is in the items.
230+
items := statistics.HistogramNeededItems.AllItems()
231+
for _, item := range items {
232+
if item.TableID == tableID {
233+
// Then, continuously check until it no longer exists or timeout.
234+
go func() {
235+
for {
236+
select {
237+
case <-ticker.C:
238+
items := statistics.HistogramNeededItems.AllItems()
239+
found := false
240+
for _, item := range items {
241+
if item.TableID == tableID {
242+
found = true
243+
}
244+
}
245+
if !found {
246+
done <- true
247+
}
248+
case <-ctx.Done():
249+
return
250+
}
251+
}
252+
}()
253+
break
254+
}
255+
}
256+
257+
select {
258+
case <-done:
259+
t.Log("Table ID has been removed from items")
260+
case <-ctx.Done():
261+
t.Fatal("Timeout: Table ID was not removed from items within the time limit")
262+
}
263+
}

0 commit comments

Comments
 (0)