Skip to content
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

executor: Optimize statements summary by avoiding memory allocation #58534

Closed
wants to merge 14 commits into from
Prev Previous commit
Next Next commit
init
Signed-off-by: crazycs520 <[email protected]>
crazycs520 committed Dec 25, 2024
commit 1c3a54c9e5cf1a0b3500c7ca973993b8c36e0699
12 changes: 11 additions & 1 deletion pkg/executor/adapter.go
Original file line number Diff line number Diff line change
@@ -2019,7 +2019,17 @@ func (a *ExecStmt) SummaryStmt(succ bool, execDetail execdetails.ExecDetails) {
if a.retryCount > 0 {
stmtExecInfo.ExecRetryTime = costTime - sessVars.DurationParse - sessVars.DurationCompile - time.Since(a.retryStartTime)
}
stmtsummaryv2.Add(stmtExecInfo)
if sessVars.CacheStmtExecInfoKey == nil {
sessVars.CacheStmtExecInfoKey = &stmtsummary.StmtSummaryByDigestKey{}
}
key := sessVars.CacheStmtExecInfoKey
key.ResetHash()
key.SchemaName = stmtExecInfo.SchemaName
key.Digest = stmtExecInfo.Digest
key.PrevDigest = stmtExecInfo.PrevSQLDigest
key.PlanDigest = stmtExecInfo.PlanDigest
key.ResourceGroupName = stmtExecInfo.ResourceGroupName
stmtsummaryv2.Add(key, stmtExecInfo)
}

// GetOriginalSQL implements StmtExecLazyInfo interface.
2 changes: 2 additions & 0 deletions pkg/sessionctx/variable/session.go
Original file line number Diff line number Diff line change
@@ -1715,6 +1715,8 @@ type SessionVars struct {

// CacheStmtExecInfo is a cache for the statement execution information, used to reduce the overhead of memory allocation.
CacheStmtExecInfo *stmtsummary.StmtExecInfo
// CacheStmtExecInfoKey is a cache too.
CacheStmtExecInfoKey *stmtsummary.StmtSummaryByDigestKey
}

// GetSessionVars implements the `SessionVarsProvider` interface.
18 changes: 9 additions & 9 deletions pkg/util/stmtsummary/statement_summary.go
Original file line number Diff line number Diff line change
@@ -60,7 +60,10 @@ type StmtSummaryByDigestKey struct {
// `prevSQL` is included in the key To distinguish different transactions.
func (key *StmtSummaryByDigestKey) Hash() []byte {
if len(key.hash) == 0 {
key.hash = make([]byte, 0, len(key.SchemaName)+len(key.Digest)+len(key.PrevDigest)+len(key.PlanDigest)+len(key.ResourceGroupName))
length := len(key.SchemaName) + len(key.Digest) + len(key.PrevDigest) + len(key.PlanDigest) + len(key.ResourceGroupName)
if cap(key.hash) < length {
key.hash = make([]byte, 0, length)
}
key.hash = append(key.hash, hack.Slice(key.Digest)...)
key.hash = append(key.hash, hack.Slice(key.SchemaName)...)
key.hash = append(key.hash, hack.Slice(key.PrevDigest)...)
@@ -70,6 +73,10 @@ func (key *StmtSummaryByDigestKey) Hash() []byte {
return key.hash
}

func (key *StmtSummaryByDigestKey) ResetHash() {
key.hash = key.hash[:0]
}

// stmtSummaryByDigestMap is a LRU cache that stores statement summaries.
type stmtSummaryByDigestMap struct {
// It's rare to read concurrently, so RWMutex is not needed.
@@ -314,7 +321,7 @@ func newStmtSummaryByDigestMap() *stmtSummaryByDigestMap {
}

// AddStatement adds a statement to StmtSummaryByDigestMap.
func (ssMap *stmtSummaryByDigestMap) AddStatement(sei *StmtExecInfo) {
func (ssMap *stmtSummaryByDigestMap) AddStatement(key *StmtSummaryByDigestKey, sei *StmtExecInfo) {
// All times are counted in seconds.
now := time.Now().Unix()

@@ -335,13 +342,6 @@ func (ssMap *stmtSummaryByDigestMap) AddStatement(sei *StmtExecInfo) {
historySize = ssMap.historySize()
}

key := &StmtSummaryByDigestKey{
SchemaName: sei.SchemaName,
Digest: sei.Digest,
PrevDigest: sei.PrevSQLDigest,
PlanDigest: sei.PlanDigest,
ResourceGroupName: sei.ResourceGroupName,
}
// Calculate hash value in advance, to reduce the time holding the lock.
key.Hash()

50 changes: 6 additions & 44 deletions pkg/util/stmtsummary/v2/stmtsummary.go
Original file line number Diff line number Diff line change
@@ -27,7 +27,6 @@ import (
"github.com/pingcap/tidb/pkg/config"
"github.com/pingcap/tidb/pkg/parser/mysql"
"github.com/pingcap/tidb/pkg/types"
"github.com/pingcap/tidb/pkg/util/hack"
"github.com/pingcap/tidb/pkg/util/kvcache"
"github.com/pingcap/tidb/pkg/util/logutil"
"github.com/pingcap/tidb/pkg/util/stmtsummary"
@@ -240,18 +239,11 @@ func (s *StmtSummary) SetRefreshInterval(v uint32) error {
// of StmtSummary. Before adding, it will check whether the current window has
// expired, and if it has expired, the window will be persisted asynchronously
// and a new window will be created to replace the current one.
func (s *StmtSummary) Add(info *stmtsummary.StmtExecInfo) {
func (s *StmtSummary) Add(k *stmtsummary.StmtSummaryByDigestKey, info *stmtsummary.StmtExecInfo) {
if s.closed.Load() {
return
}

k := &stmtKey{
schemaName: info.SchemaName,
digest: info.Digest,
prevDigest: info.PrevSQLDigest,
planDigest: info.PlanDigest,
resourceGroupName: info.ResourceGroupName,
}
k.Hash() // Calculate hash value in advance, to reduce the time holding the window lock.

// Add info to the current statistics window.
@@ -431,7 +423,7 @@ func newStmtWindow(begin time.Time, capacity uint) *stmtWindow {
r := v.(*lockedStmtRecord)
r.Lock()
defer r.Unlock()
w.evicted.add(k.(*stmtKey), r.StmtRecord)
w.evicted.add(k.(*stmtsummary.StmtSummaryByDigestKey), r.StmtRecord)
})
return w
}
@@ -446,36 +438,6 @@ type stmtStorage interface {
sync() error
}

// stmtKey defines key for stmtElement.
type stmtKey struct {
// Same statements may appear in different schema, but they refer to different tables.
schemaName string
digest string
// The digest of the previous statement.
prevDigest string
// The digest of the plan of this SQL.
planDigest string
// `resourceGroupName` is the resource group's name of this statement is bind to.
resourceGroupName string
// `hash` is the hash value of this object.
hash []byte
}

// Hash implements SimpleLRUCache.Key.
// Only when current SQL is `commit` do we record `prevSQL`. Otherwise, `prevSQL` is empty.
// `prevSQL` is included in the key To distinguish different transactions.
func (k *stmtKey) Hash() []byte {
if len(k.hash) == 0 {
k.hash = make([]byte, 0, len(k.schemaName)+len(k.digest)+len(k.prevDigest)+len(k.planDigest)+len(k.resourceGroupName))
k.hash = append(k.hash, hack.Slice(k.digest)...)
k.hash = append(k.hash, hack.Slice(k.schemaName)...)
k.hash = append(k.hash, hack.Slice(k.prevDigest)...)
k.hash = append(k.hash, hack.Slice(k.planDigest)...)
k.hash = append(k.hash, hack.Slice(k.resourceGroupName)...)
}
return k.hash
}

type stmtEvicted struct {
sync.Mutex
keys map[string]struct{}
@@ -494,7 +456,7 @@ func newStmtEvicted() *stmtEvicted {
}
}

func (e *stmtEvicted) add(key *stmtKey, record *StmtRecord) {
func (e *stmtEvicted) add(key *stmtsummary.StmtSummaryByDigestKey, record *StmtRecord) {
if key == nil || record == nil {
return
}
@@ -533,11 +495,11 @@ func (*mockStmtStorage) sync() error {
/* Public proxy functions between v1 and v2 */

// Add wraps GlobalStmtSummary.Add and stmtsummary.StmtSummaryByDigestMap.AddStatement.
func Add(stmtExecInfo *stmtsummary.StmtExecInfo) {
func Add(key *stmtsummary.StmtSummaryByDigestKey, stmtExecInfo *stmtsummary.StmtExecInfo) {
if config.GetGlobalConfig().Instance.StmtSummaryEnablePersistent {
GlobalStmtSummary.Add(stmtExecInfo)
GlobalStmtSummary.Add(key, stmtExecInfo)
} else {
stmtsummary.StmtSummaryByDigestMap.AddStatement(stmtExecInfo)
stmtsummary.StmtSummaryByDigestMap.AddStatement(key, stmtExecInfo)
}
}