diff --git a/pkg/statistics/handle/globalstats/global_stats.go b/pkg/statistics/handle/globalstats/global_stats.go index c366b96a51c4f..4eff64fc0961b 100644 --- a/pkg/statistics/handle/globalstats/global_stats.go +++ b/pkg/statistics/handle/globalstats/global_stats.go @@ -305,10 +305,15 @@ func blockingMergePartitionStats2GlobalStats( } // FMSketch use many memory, so we first deal with it and then destroy it. // Merge FMSketch. + // NOTE: allFms maybe contain empty. globalStats.Fms[i] = allFms[i][0] for j := 1; j < len(allFms[i]); j++ { - globalStats.Fms[i].MergeFMSketch(allFms[i][j]) - allFms[i][j].DestroyAndPutToPool() + if globalStats.Fms[i] == nil { + globalStats.Fms[i] = allFms[i][j] + } else { + globalStats.Fms[i].MergeFMSketch(allFms[i][j]) + allFms[i][j].DestroyAndPutToPool() + } } // Update the global NDV. diff --git a/tests/realtikvtest/statisticstest/statistics_test.go b/tests/realtikvtest/statisticstest/statistics_test.go index 6dcc5d65f5e50..5535a387e73d4 100644 --- a/tests/realtikvtest/statisticstest/statistics_test.go +++ b/tests/realtikvtest/statisticstest/statistics_test.go @@ -185,3 +185,50 @@ func TestNewCollationStatsWithPrefixIndex(t *testing.T) { "1 3 15 0 2 0", )) } + +func TestBlockMergeFMSketch(t *testing.T) { + store := realtikvtest.CreateMockStoreAndSetup(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set @@tidb_enable_async_merge_global_stats=OFF;") + defer func() { + tk.MustExec("set @@tidb_enable_async_merge_global_stats=ON;") + }() + checkFMSketch(tk) +} + +func TestAsyncMergeFMSketch(t *testing.T) { + store := realtikvtest.CreateMockStoreAndSetup(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set @@tidb_enable_async_merge_global_stats=ON;") + checkFMSketch(tk) +} + +func checkFMSketch(tk *testkit.TestKit) { + tk.MustExec(`CREATE TABLE employees (id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,fname VARCHAR(25) NOT NULL,lname VARCHAR(25) NOT NULL,store_id INT NOT NULL,department_id INT NOT NULL +) PARTITION BY RANGE(id) ( + PARTITION p0 VALUES LESS THAN (5), + PARTITION p1 VALUES LESS THAN (10), + PARTITION p2 VALUES LESS THAN (15), + PARTITION p3 VALUES LESS THAN MAXVALUE +);`) + tk.MustExec(`INSERT INTO employees(FNAME,LNAME,STORE_ID,DEPARTMENT_ID) VALUES + ('Bob', 'Taylor', 3, 2), ('Frank', 'Williams', 1, 2), + ('Ellen', 'Johnson', 3, 4), ('Jim', 'Smith', 2, 4), + ('Mary', 'Jones', 1, 1), ('Linda', 'Black', 2, 3), + ('Ed', 'Jones', 2, 1), ('June', 'Wilson', 3, 1), + ('Andy', 'Smith', 1, 3), ('Lou', 'Waters', 2, 4), + ('Jill', 'Stone', 1, 4), ('Roger', 'White', 3, 2), + ('Howard', 'Andrews', 1, 2), ('Fred', 'Goldberg', 3, 3), + ('Barbara', 'Brown', 2, 3), ('Alice', 'Rogers', 2, 2), + ('Mark', 'Morgan', 3, 3), ('Karen', 'Cole', 3, 2);`) + tk.MustExec("ANALYZE TABLE employees;") + tk.MustExec("select * from employees;") + tk.MustExec("alter table employees truncate partition p0;") + tk.MustExec("select * from employees;") + tk.MustExec("analyze table employees partition p3;") + tk.MustExec("select * from employees;") + tk.MustQuery(`SHOW STATS_HISTOGRAMS WHERE TABLE_NAME='employees' and partition_name="global" and column_name="id"`).CheckAt([]int{6}, [][]any{ + {"14"}}) +}