Skip to content

planner, statistics: build the global statistics for the partition table#22472

Closed
Reminiscent wants to merge 8 commits intopingcap:masterfrom
Reminiscent:BuildGlobalStats
Closed

planner, statistics: build the global statistics for the partition table#22472
Reminiscent wants to merge 8 commits intopingcap:masterfrom
Reminiscent:BuildGlobalStats

Conversation

@Reminiscent
Copy link
Contributor

@Reminiscent Reminiscent commented Jan 21, 2021

What problem does this PR solve?

Issue Number: related: #18551

Problem Summary:
Build the global statistics for the partition tables when we execute the analyze statement.

What is changed and how it works?

Proposal (in Chinese)

What's Changed(In the Dynamic-Only mode):
In the origin implementation, we build analyze task that "collect multi partitions and save as a table" mode. You can see PR#19846, PR#19899 and PR#20271 for more details.

In this PR, we build the analyze task that "collect a partition save a partition" first. And then merge the partition-level stats which belong to the same partition table to get the global-level stats.

We will use the changed variable analyzeTableID for more explanation. You can see this comment for more details.

How it Works(In the Dynamic-Only mode):

  1. When we build the analyze task, we build every task for every partition. And record the table ID to which table the partition belongs to.
  2. If there are some tasks related to the partition table, we will record the partition table ID and index ID.
  3. We use the partition table ID to get the corresponding partition-level stats from the storage. And merge them to build the global-level stats.

Related changes

  • PR to update pingcap/docs/pingcap/docs-cn:
  • Need to cherry-pick to the release branch

Check List

Tests

  • Unit test
  • Integration test

Side effects

  • Performance regression
    • Consumes more CPU
    • Consumes more MEM
  • Breaking backward compatibility

Release note

  • Build the global statistics for the partition table

@sre-bot
Copy link
Contributor

sre-bot commented Jan 21, 2021

Please follow PR Title Format:

  • pkg [, pkg2, pkg3]: what's changed

Or if the count of mainly changed packages are more than 3, use

  • *: what's changed

@sre-bot
Copy link
Contributor

sre-bot commented Jan 21, 2021

Please follow PR Title Format:

  • pkg [, pkg2, pkg3]: what's changed

Or if the count of mainly changed packages are more than 3, use

  • *: what's changed

} else {
e.tasks = append(e.tasks, b.buildAnalyzeIndexPushdown(task, v.Opts, autoAnalyze))
}
e.tasks = append(e.tasks, b.buildAnalyzeIndexPushdown(task, v.Opts, autoAnalyze))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: Need more investigation on when to use buildAnalyzeFastIndex.

Comment on lines 763 to 768
type AnalyzeTableID struct {
PersistID int64
CollectIDs []int64
PersistID int64
// FatherID just used in the partition table.
// It represents the ID of the table to which the partition belongs.
FatherID int64
}
Copy link
Contributor Author

@Reminiscent Reminiscent Jan 21, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Version1(The 'static-only' mode): No global-level stats, each partition has its own stats.
Version2: Only global-level stats, no partition-level stats.
Version3(This PR): Both have partition-level stats and global-level stats, and the global-level stats are merged from the partition-level stats

Give an example to make the meaning of these IDs more clear.

Now, we have table t(ID = 1). And the table t has three partitions p1(ID = 2), p2(ID = 3) and p3(ID = 4).

In version1, the analyze request has three tasks: AnalyzeTableID{PersistID = 2, CollectIDs = {2}}, AnalyzeTableID{PersistID = 3, CollectIDs = {3}} , AnalyzeTableID{PersistID = 4, CollectIDs = {4}}.

In version2, the analyze request has only one task: AnalyzeTableID{PersistID = 1, CollectIDs = {2, 3, 4}}.

In version3, the analyze request has three tasks: AnalyzeTableID{PersistID = 2, FatherID= 1}, AnalyzeTableID{PersistID = 3, FatherID= 1} , AnalyzeTableID{PersistID = 4, FatherID= 1}.

@sre-bot
Copy link
Contributor

sre-bot commented Jan 22, 2021

Please follow PR Title Format:

  • pkg [, pkg2, pkg3]: what's changed

Or if the count of mainly changed packages are more than 3, use

  • *: what's changed

@Reminiscent Reminiscent changed the title [WIP] Build the global statistics for the partition table planner, statistics: build the global statistics for the partition table Jan 22, 2021
@Reminiscent Reminiscent marked this pull request as ready for review January 25, 2021 03:11
@Reminiscent Reminiscent requested review from a team as code owners January 25, 2021 03:11
@Reminiscent Reminiscent requested review from XuHuaiyu and removed request for a team January 25, 2021 03:11
Comment on lines +472 to +473
"IndexReader_8 2.80 root index:IndexRangeScan_7",
"└─IndexRangeScan_7 2.80 cop[tikv] table:t3, partition:p1, index:k(v) range:[3,3], keep order:false",
Copy link
Contributor Author

@Reminiscent Reminiscent Jan 25, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: Need more investigation on it is possible to use partition-level stats when there is only a single partition in the where condition.

@Reminiscent
Copy link
Contributor Author

Reminiscent commented Jan 25, 2021

The reason why the test fails now is that the merged function of other statistical information like histogram and topN has not been completed, we need to wait until they are all completed.

@ti-srebot ti-srebot added the type/enhancement The issue or PR belongs to an enhancement. label Jan 25, 2021
@ti-srebot
Copy link
Contributor

These labels are not found sig/ planner.

@ti-srebot ti-srebot added the sig/planner SIG: Planner label Jan 25, 2021
Comment on lines +200 to +217
func (t *Table) getColumnStatsInfo(colID int64) (*Histogram, *CMSketch, *TopN) {
colStatsInfo := t.Columns[colID]
return colStatsInfo.Histogram.Copy(), colStatsInfo.CMSketch.Copy(), colStatsInfo.TopN.Copy()
}

func (t *Table) getIndexStatsInfo(idxID int64) (*Histogram, *CMSketch, *TopN) {
idxStatsInfo := t.Indices[idxID]
return idxStatsInfo.Histogram.Copy(), idxStatsInfo.CMSketch.Copy(), idxStatsInfo.TopN.Copy()
}

// GetStatsInfo returns their statistics according to the ID of the column or index, including histogram, CMSketch and TopN.
func (t *Table) GetStatsInfo(ID int64, isIndex int) (*Histogram, *CMSketch, *TopN) {
if isIndex == 0 {
return t.getColumnStatsInfo(ID)
}
return t.getIndexStatsInfo(ID)
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getColumnStatsInfo and getIndexStatsInfo are simple and not used by other functions directly, so how about merging these 3 functions into 1?

return
}
tableInfo := partitionTable.Meta()
partitionStats, err := h.tableStatsFromStorage(tableInfo, partitionID, false, nil)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we load it from cache first?

Comment on lines +229 to +235
type GlobalStats struct {
num int
count int64
hg []*statistics.Histogram
cms []*statistics.CMSketch
topN []*statistics.TopN
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about exposing this struct's fields and removing its methods, which may make it clearer?

succ = false
break
}
err = statsHandle.SaveStatsToStorage(info.tableID, globalStatsCount, info.isIndex, hg, cms, topN, info.statsVersion, 1)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we update the cache after saving?

@Reminiscent
Copy link
Contributor Author

To make it easier to review, we will split this PR into multiple sub-PRs.

Comment on lines -502 to +554
reqBuilder := builder.SetHandleRangesForTables(e.ctx.GetSessionVars().StmtCtx, e.tableID.CollectIDs, e.handleCols != nil && !e.handleCols.IsInt(), ranges, nil)
reqBuilder := builder.SetHandleRangesForTables(e.ctx.GetSessionVars().StmtCtx, []int64{e.tableID.FatherID}, e.handleCols != nil && !e.handleCols.IsInt(), ranges, nil)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why we use FatherID rather than PersistID here? I'm kind of confused. Thx~

Copy link
Contributor Author

@Reminiscent Reminiscent Jan 27, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please see this PR. It has some differences with this PR. Thanks~

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component/statistics sig/execution SIG execution sig/planner SIG: Planner type/enhancement The issue or PR belongs to an enhancement.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants