diff --git a/pkg/parser/model/model.go b/pkg/parser/model/model.go index 5cac81a9664fa..9d3cdecdf69d2 100644 --- a/pkg/parser/model/model.go +++ b/pkg/parser/model/model.go @@ -857,6 +857,16 @@ func (t *TableInfo) FindIndexByName(idxName string) *IndexInfo { return nil } +// FindColumnByID finds ColumnInfo by id. +func (t *TableInfo) FindColumnByID(id int64) *ColumnInfo { + for _, col := range t.Columns { + if col.ID == id { + return col + } + } + return nil +} + // FindIndexByID finds index by id. func (t *TableInfo) FindIndexByID(id int64) *IndexInfo { for _, idx := range t.Indices { diff --git a/pkg/statistics/handle/syncload/stats_syncload.go b/pkg/statistics/handle/syncload/stats_syncload.go index 29eb559009471..ec0c2dba3ed31 100644 --- a/pkg/statistics/handle/syncload/stats_syncload.go +++ b/pkg/statistics/handle/syncload/stats_syncload.go @@ -33,6 +33,7 @@ import ( "github.com/pingcap/tidb/pkg/statistics" "github.com/pingcap/tidb/pkg/statistics/handle/storage" statstypes "github.com/pingcap/tidb/pkg/statistics/handle/types" + "github.com/pingcap/tidb/pkg/table" "github.com/pingcap/tidb/pkg/types" "github.com/pingcap/tidb/pkg/util" "github.com/pingcap/tidb/pkg/util/intest" @@ -332,11 +333,6 @@ func (s *statsSyncLoad) handleOneItemTask(task *statstypes.NeededItemTask) (err } else { // Now, we cannot init the column info in the ColAndIdxExistenceMap when to disable lite-init-stats. // so we have to get the column info from the domain. - is := sctx.GetDomainInfoSchema().(infoschema.InfoSchema) - tblInfo, ok = s.statsHandle.TableInfoByID(is, item.TableID) - if !ok { - return nil - } wrapper.colInfo = tblInfo.Meta().GetColumnByID(item.ID) } // If this column is not analyzed yet and we don't have it in memory. @@ -348,7 +344,7 @@ func (s *statsSyncLoad) handleOneItemTask(task *statstypes.NeededItemTask) (err Histogram: *statistics.NewHistogram(item.ID, 0, 0, 0, &wrapper.colInfo.FieldType, 0, 0), IsHandle: isPkIsHandle && mysql.HasPriKeyFlag(wrapper.colInfo.GetFlag()), } - s.updateCachedItem(item, wrapper.col, wrapper.idx, task.Item.FullLoad) + s.updateCachedItem(tblInfo, item, wrapper.col, wrapper.idx, task.Item.FullLoad) return nil } } @@ -369,7 +365,7 @@ func (s *statsSyncLoad) handleOneItemTask(task *statstypes.NeededItemTask) (err } metrics.ReadStatsHistogram.Observe(float64(time.Since(t).Milliseconds())) if needUpdate { - s.updateCachedItem(item, wrapper.col, wrapper.idx, task.Item.FullLoad) + s.updateCachedItem(tblInfo, item, wrapper.col, wrapper.idx, task.Item.FullLoad) } return nil } @@ -542,7 +538,7 @@ func (*statsSyncLoad) writeToResultChan(resultCh chan stmtctx.StatsLoadResult, r } // updateCachedItem updates the column/index hist to global statsCache. -func (s *statsSyncLoad) updateCachedItem(item model.TableItemID, colHist *statistics.Column, idxHist *statistics.Index, fullLoaded bool) (updated bool) { +func (s *statsSyncLoad) updateCachedItem(tblInfo table.Table, item model.TableItemID, colHist *statistics.Column, idxHist *statistics.Index, fullLoaded bool) (updated bool) { s.StatsLoad.Lock() defer s.StatsLoad.Unlock() // Reload the latest stats cache, otherwise the `updateStatsCache` may fail with high probability, because functions @@ -551,6 +547,22 @@ func (s *statsSyncLoad) updateCachedItem(item model.TableItemID, colHist *statis if !ok { return false } + if !tbl.ColAndIdxExistenceMap.Checked() { + tbl = tbl.Copy() + for _, col := range tbl.HistColl.GetColSlice() { + if tblInfo.Meta().FindColumnByID(col.ID) == nil { + tbl.HistColl.DelCol(col.ID) + tbl.ColAndIdxExistenceMap.DeleteColAnalyzed(col.ID) + } + } + for _, idx := range tbl.HistColl.GetIdxSlice() { + if tblInfo.Meta().FindIndexByID(idx.ID) == nil { + tbl.HistColl.DelIdx(idx.ID) + tbl.ColAndIdxExistenceMap.DeleteIdxAnalyzed(idx.ID) + } + } + tbl.ColAndIdxExistenceMap.SetChecked() + } if !item.IsIndex && colHist != nil { c := tbl.GetCol(item.ID) // - If the stats is fully loaded, diff --git a/pkg/statistics/table.go b/pkg/statistics/table.go index cda99ba87697f..3944a368327cb 100644 --- a/pkg/statistics/table.go +++ b/pkg/statistics/table.go @@ -90,6 +90,16 @@ type ColAndIdxExistenceMap struct { idxAnalyzed map[int64]bool } +// DeleteColAnalyzed deletes the column with the given id. +func (m *ColAndIdxExistenceMap) DeleteColAnalyzed(id int64) { + delete(m.colAnalyzed, id) +} + +// DeleteIdxAnalyzed deletes the index with the given id. +func (m *ColAndIdxExistenceMap) DeleteIdxAnalyzed(id int64) { + delete(m.idxAnalyzed, id) +} + // Checked returns whether the map has been checked. func (m *ColAndIdxExistenceMap) Checked() bool { return m.checked @@ -356,6 +366,15 @@ func (coll *HistColl) StableOrderColSlice() []*Column { return cols } +// GetColSlice returns a slice of columns without order. +func (coll *HistColl) GetColSlice() []*Column { + cols := make([]*Column, 0, len(coll.columns)) + for _, col := range coll.columns { + cols = append(cols, col) + } + return cols +} + // StableOrderIdxSlice returns a slice of indices in stable order. func (coll *HistColl) StableOrderIdxSlice() []*Index { idxs := make([]*Index, 0, len(coll.indices)) @@ -368,6 +387,15 @@ func (coll *HistColl) StableOrderIdxSlice() []*Index { return idxs } +// GetIdxSlice returns a slice of indices without order. +func (coll *HistColl) GetIdxSlice() []*Index { + idxs := make([]*Index, 0, len(coll.indices)) + for _, idx := range coll.indices { + idxs = append(idxs, idx) + } + return idxs +} + // SetAllIndexFullLoadForBootstrap sets all indices' stats loaded status to full load for bootstrap. func (coll *HistColl) SetAllIndexFullLoadForBootstrap() { for _, idx := range coll.indices { @@ -807,6 +835,9 @@ func (t *Table) ColumnIsLoadNeeded(id int64, fullLoad bool) (*Column, bool, bool if !ok { return nil, true, true } + if t.ColAndIdxExistenceMap.Checked() { + return nil, true, true + } hasAnalyzed := t.ColAndIdxExistenceMap.HasAnalyzed(id, false) // If it's not analyzed yet. @@ -833,7 +864,7 @@ func (t *Table) ColumnIsLoadNeeded(id int64, fullLoad bool) (*Column, bool, bool func (t *Table) IndexIsLoadNeeded(id int64) (*Index, bool) { idx, ok := t.indices[id] // If the index is not in the memory, and we have its stats in the storage. We need to trigger the load. - if !ok && t.ColAndIdxExistenceMap.HasAnalyzed(id, true) { + if !ok && (t.ColAndIdxExistenceMap.HasAnalyzed(id, true) || !t.ColAndIdxExistenceMap.Checked()) { return nil, true } // If the index is in the memory, we check its embedded func.