-
Notifications
You must be signed in to change notification settings - Fork 5.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
plan: add cache for statistics table #2398
Changes from 2 commits
e05bab3
3d7ed35
143babb
5ee5b15
c743408
bb0ba43
26560e4
74d43c7
5ef6023
4cc7b71
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,6 +20,7 @@ import ( | |
"github.com/ngaut/log" | ||
"github.com/pingcap/tidb/ast" | ||
"github.com/pingcap/tidb/expression" | ||
"github.com/pingcap/tidb/meta" | ||
"github.com/pingcap/tidb/model" | ||
"github.com/pingcap/tidb/mysql" | ||
"github.com/pingcap/tidb/plan/statistics" | ||
|
@@ -908,8 +909,39 @@ func (b *planBuilder) buildTableDual() LogicalPlan { | |
} | ||
|
||
func (b *planBuilder) getTableStats(table *model.TableInfo) *statistics.Table { | ||
// TODO: Currently we always return a pseudo table for good performance. We will use a cache in future. | ||
return statistics.PseudoTable(table) | ||
tbl := statistics.GetStatisticsTableCache(table) | ||
if tbl != nil { | ||
return tbl | ||
} | ||
txn := b.ctx.Txn() | ||
if txn == nil { | ||
return statistics.PseudoTable(table) | ||
} | ||
m := meta.NewMeta(txn) | ||
tpb, err := m.GetTableStats(table.ID) | ||
if err != nil { | ||
return statistics.PseudoTable(table) | ||
} | ||
// This table has no statistics table, we give it a pseudo one and save in cache. | ||
if tpb == nil { | ||
tbl = statistics.PseudoTable(table) | ||
tbl.TS = int64(txn.StartTS()) | ||
statistics.SetStatisticsTableCache(table.ID, tbl) | ||
return tbl | ||
} | ||
tbl, err = statistics.TableFromPB(table, tpb) | ||
// Error is not nil may mean that there are some ddl changes on this table, so the origin | ||
// statistics can not be used any more, we give it a pseudo one and save in cache. | ||
if err != nil { | ||
log.Errorf("Error occured when convert pb table for %s", table.Name.O) | ||
tbl = statistics.PseudoTable(table) | ||
tbl.TS = int64(txn.StartTS()) | ||
statistics.SetStatisticsTableCache(table.ID, tbl) | ||
return statistics.PseudoTable(table) | ||
} | ||
tbl.TS = int64(txn.StartTS()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should not use the build time field to record lookup time. |
||
statistics.SetStatisticsTableCache(table.ID, tbl) | ||
return tbl | ||
} | ||
|
||
func (b *planBuilder) buildDataSource(tn *ast.TableName) LogicalPlan { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
// Copyright 2017 PingCAP, Inc. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package statistics | ||
|
||
import ( | ||
"sync" | ||
"time" | ||
|
||
"github.com/pingcap/tidb/model" | ||
"github.com/pingcap/tidb/store/tikv/oracle" | ||
) | ||
|
||
type tableCache struct { | ||
m sync.RWMutex | ||
cache map[int64]*Table | ||
} | ||
|
||
var tblCache = tableCache{cache: map[int64]*Table{}} | ||
|
||
// expireDuration is 1 hour. | ||
var expireDuration int64 = 60 * 60 * 1000 | ||
|
||
func tableCacheExpired(tbl *Table) bool { | ||
duration := oracle.GetPhysical(time.Now()) - oracle.ExtractPhysical(uint64(tbl.TS)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
if duration >= expireDuration { | ||
return true | ||
} | ||
return false | ||
} | ||
|
||
// GetStatisticsTableCache retrieves the statistics table from cache. | ||
func GetStatisticsTableCache(tblInfo *model.TableInfo) *Table { | ||
tblCache.m.RLock() | ||
statTbl, ok := tblCache.cache[tblInfo.ID] | ||
tblCache.m.RUnlock() | ||
// Here we check the TableInfo because there may be some ddl changes in the duration period. | ||
// Also, we rely on the fact that TableInfo will not be same if and only if there are ddl changes. | ||
if !ok || tblInfo != statTbl.info || tableCacheExpired(statTbl) { | ||
return nil | ||
} | ||
return statTbl | ||
} | ||
|
||
// SetStatisticsTableCache sets the statistics table cache. | ||
func SetStatisticsTableCache(id int64, tbl *Table) { | ||
tblCache.m.Lock() | ||
tblCache.cache[id] = tbl | ||
tblCache.m.Unlock() | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think move cache refill logic to
statistics
package is better.And If the the stats for a table is always nil, we don't want to get it from kv every time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried, but it will introduce circle dependency, because
meta/meta.go
importsstatistics
package. And if stats table is nil, I will save a pseduo one in cache, thus we will not get it from kv every time.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The cache can be implemented in another package.
like
statscache
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
plan/statscache
orplan/statistics/statscache
?