From df83b9a060a04979a2a3b580fcd6bc5ea63194bc Mon Sep 17 00:00:00 2001 From: qw4990 Date: Fri, 29 Dec 2023 14:51:24 +0800 Subject: [PATCH] fixup --- statistics/handle/update.go | 8 +++-- statistics/handle/update_test.go | 58 ++++++++++++++++++++++++++++++-- 2 files changed, 62 insertions(+), 4 deletions(-) diff --git a/statistics/handle/update.go b/statistics/handle/update.go index ac9b11254a9ce..a17ca7031854f 100644 --- a/statistics/handle/update.go +++ b/statistics/handle/update.go @@ -537,9 +537,13 @@ func (h *Handle) dumpTableStatCountToKV(id int64, delta variable.TableDelta) (up updateStatsMeta := func(id int64) error { var err error if delta.Delta < 0 { - _, err = exec.ExecuteInternal(ctx, "update mysql.stats_meta set version = %?, count = count - %?, modify_count = modify_count + %? where table_id = %? and count >= %?", startTS, -delta.Delta, delta.Count, id, -delta.Delta) + // use INSERT INTO ... ON DUPLICATE KEY UPDATE here to fill missing stats_meta. + _, err = exec.ExecuteInternal(ctx, "insert into mysql.stats_meta (version, table_id, modify_count, count) values (%?, %?, %?, 0) on duplicate key "+ + "update version = values(version), modify_count = modify_count + values(modify_count), count = if(count > %?, count - %?, 0)", startTS, id, delta.Count, -delta.Delta, -delta.Delta) } else { - _, err = exec.ExecuteInternal(ctx, "update mysql.stats_meta set version = %?, count = count + %?, modify_count = modify_count + %? where table_id = %?", startTS, delta.Delta, delta.Count, id) + // use INSERT INTO ... ON DUPLICATE KEY UPDATE here to fill missing stats_meta. + _, err = exec.ExecuteInternal(ctx, "insert into mysql.stats_meta (version, table_id, modify_count, count) values (%?, %?, %?, %?) on duplicate key "+ + "update version = values(version), modify_count = modify_count + values(modify_count), count = count + values(count)", startTS, id, delta.Count, delta.Delta) } statsVer = startTS return errors.Trace(err) diff --git a/statistics/handle/update_test.go b/statistics/handle/update_test.go index 174c8d48b139e..d7e67205bd607 100644 --- a/statistics/handle/update_test.go +++ b/statistics/handle/update_test.go @@ -1160,13 +1160,15 @@ func TestOutOfOrderUpdate(t *testing.T) { testKit.MustExec("delete from t") require.NoError(t, h.DumpStatsDeltaToKV(handle.DumpAll)) - testKit.MustQuery("select count from mysql.stats_meta").Check(testkit.Rows("1")) + // If count < -Delta, then update count to 0. + // Check https://github.com/pingcap/tidb/pull/38301#discussion_r1094050951 for details. + testKit.MustQuery(fmt.Sprintf("select count from mysql.stats_meta where table_id = %d", tableInfo.ID)).Check(testkit.Rows("0")) // Now another tidb has updated the delta info. testKit.MustExec(fmt.Sprintf("update mysql.stats_meta set count = 3 where table_id = %d", tableInfo.ID)) require.NoError(t, h.DumpStatsDeltaToKV(handle.DumpAll)) - testKit.MustQuery("select count from mysql.stats_meta").Check(testkit.Rows("0")) + testKit.MustQuery(fmt.Sprintf("select count from mysql.stats_meta where table_id = %d", tableInfo.ID)).Check(testkit.Rows("3")) } func TestUpdateStatsByLocalFeedback(t *testing.T) { @@ -1231,6 +1233,58 @@ func TestUpdateStatsByLocalFeedback(t *testing.T) { h.UpdateStatsByLocalFeedback(dom.InfoSchema()) } +func TestFillMissingStatsMeta(t *testing.T) { + store, dom, clean := testkit.CreateMockStoreAndDomain(t) + defer clean() + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t1 (a int, b int)") + tk.MustExec("create table t2 (a int, b int) partition by range (a) (partition p0 values less than (10), partition p1 values less than (maxvalue))") + + tk.MustQuery("select * from mysql.stats_meta").Check(testkit.Rows()) + + is := dom.InfoSchema() + tbl1, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) + require.NoError(t, err) + tbl1ID := tbl1.Meta().ID + tbl2, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t2")) + require.NoError(t, err) + tbl2Info := tbl2.Meta() + tbl2ID := tbl2Info.ID + require.Len(t, tbl2Info.Partition.Definitions, 2) + p0ID := tbl2Info.Partition.Definitions[0].ID + p1ID := tbl2Info.Partition.Definitions[1].ID + h := dom.StatsHandle() + + checkStatsMeta := func(id int64, expectedModifyCount, expectedCount string) int64 { + rows := tk.MustQuery(fmt.Sprintf("select version, modify_count, count from mysql.stats_meta where table_id = %v", id)).Rows() + require.Len(t, rows, 1) + ver, err := strconv.ParseInt(rows[0][0].(string), 10, 64) + require.NoError(t, err) + require.Equal(t, expectedModifyCount, rows[0][1]) + require.Equal(t, expectedCount, rows[0][2]) + return ver + } + + tk.MustExec("insert into t1 values (1, 2), (3, 4)") + require.NoError(t, h.DumpStatsDeltaToKV(handle.DumpAll)) + ver1 := checkStatsMeta(tbl1ID, "2", "2") + tk.MustExec("delete from t1 where a = 1") + require.NoError(t, h.DumpStatsDeltaToKV(handle.DumpAll)) + ver2 := checkStatsMeta(tbl1ID, "3", "1") + require.Greater(t, ver2, ver1) + + tk.MustExec("insert into t2 values (1, 2), (3, 4)") + require.NoError(t, h.DumpStatsDeltaToKV(handle.DumpAll)) + checkStatsMeta(p0ID, "2", "2") + globalVer1 := checkStatsMeta(tbl2ID, "2", "2") + tk.MustExec("insert into t2 values (11, 12)") + require.NoError(t, h.DumpStatsDeltaToKV(handle.DumpAll)) + checkStatsMeta(p1ID, "1", "1") + globalVer2 := checkStatsMeta(tbl2ID, "3", "3") + require.Greater(t, globalVer2, globalVer1) +} + func TestUpdatePartitionStatsByLocalFeedback(t *testing.T) { store, dom, clean := testkit.CreateMockStoreAndDomain(t) defer clean()