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 statement summary performance by avoiding heap memory allocation #58023

Merged
merged 12 commits into from
Dec 11, 2024
155 changes: 80 additions & 75 deletions pkg/executor/adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -1676,7 +1676,7 @@ func (a *ExecStmt) LogSlowQuery(txnTS uint64, succ bool, hasMoreResults bool) {
TimeOptimize: sessVars.DurationOptimization,
TimeWaitTS: sessVars.DurationWaitTS,
IndexNames: indexNames,
CopTasks: &copTaskInfo,
CopTasks: copTaskInfo,
ExecDetail: execDetail,
MemMax: memMax,
DiskMax: diskMax,
Expand Down Expand Up @@ -1797,12 +1797,7 @@ func GetResultRowsCount(stmtCtx *stmtctx.StatementContext, p base.Plan) int64 {
if runtimeStatsColl == nil {
return 0
}
rootPlanID := p.ID()
if !runtimeStatsColl.ExistsRootStats(rootPlanID) {
return 0
}
rootStats := runtimeStatsColl.GetRootStats(rootPlanID)
return rootStats.GetActRows()
return runtimeStatsColl.GetPlanActRows(p.ID())
}

// getFlatPlan generates a FlatPhysicalPlan from the plan stored in stmtCtx.plan,
Expand Down Expand Up @@ -1936,37 +1931,11 @@ func (a *ExecStmt) SummaryStmt(succ bool) {
}
sessVars.SetPrevStmtDigest(digest.String())

// No need to encode every time, so encode lazily.
planGenerator := func() (p string, h string, e any) {
defer func() {
e = recover()
if e != nil {
logutil.BgLogger().Warn("fail to generate plan info",
zap.Stack("backtrace"),
zap.Any("error", e))
}
}()
p, h = getEncodedPlan(stmtCtx, !sessVars.InRestrictedSQL)
return
}
var binPlanGen func() string
if variable.GenerateBinaryPlan.Load() {
binPlanGen = func() string {
binPlan := getBinaryPlan(a.Ctx)
return binPlan
}
}
// Generating plan digest is slow, only generate it once if it's 'Point_Get'.
// If it's a point get, different SQLs leads to different plans, so SQL digest
// is enough to distinguish different plans in this case.
var planDigest string
var planDigestGen func() string
if a.Plan.TP() == plancodec.TypePointGet {
planDigestGen = func() string {
_, planDigest := GetPlanDigest(stmtCtx)
return planDigest.String()
}
} else {
if a.Plan.TP() != plancodec.TypePointGet {
_, tmp := GetPlanDigest(stmtCtx)
planDigest = tmp.String()
}
Expand All @@ -1975,7 +1944,6 @@ func (a *ExecStmt) SummaryStmt(succ bool) {
copTaskInfo := stmtCtx.CopTasksDetails()
memMax := sessVars.MemTracker.MaxConsumed()
diskMax := sessVars.DiskTracker.MaxConsumed()
sql := a.getLazyStmtText()
var stmtDetail execdetails.StmtExecDetails
stmtDetailRaw := a.GoCtx.Value(execdetails.StmtExecDetailKey)
if stmtDetailRaw != nil {
Expand Down Expand Up @@ -2011,52 +1979,89 @@ func (a *ExecStmt) SummaryStmt(succ bool) {
keyspaceID = uint32(a.Ctx.GetStore().GetCodec().GetKeyspaceID())
}

stmtExecInfo := &stmtsummary.StmtExecInfo{
SchemaName: strings.ToLower(sessVars.CurrentDB),
OriginalSQL: &sql,
Charset: charset,
Collation: collation,
NormalizedSQL: normalizedSQL,
Digest: digest.String(),
PrevSQL: prevSQL,
PrevSQLDigest: prevSQLDigest,
PlanGenerator: planGenerator,
BinaryPlanGenerator: binPlanGen,
PlanDigest: planDigest,
PlanDigestGen: planDigestGen,
User: userString,
TotalLatency: costTime,
ParseLatency: sessVars.DurationParse,
CompileLatency: sessVars.DurationCompile,
StmtCtx: stmtCtx,
CopTasks: &copTaskInfo,
ExecDetail: &execDetail,
MemMax: memMax,
DiskMax: diskMax,
StartTime: sessVars.StartTime,
IsInternal: sessVars.InRestrictedSQL,
Succeed: succ,
PlanInCache: sessVars.FoundInPlanCache,
PlanInBinding: sessVars.FoundInBinding,
ExecRetryCount: a.retryCount,
StmtExecDetails: stmtDetail,
ResultRows: resultRows,
TiKVExecDetails: tikvExecDetail,
Prepared: a.isPreparedStmt,
KeyspaceName: keyspaceName,
KeyspaceID: keyspaceID,
RUDetail: ruDetail,
ResourceGroupName: sessVars.StmtCtx.ResourceGroupName,
CPUUsages: sessVars.SQLCPUUsages.GetCPUUsages(),

PlanCacheUnqualified: sessVars.StmtCtx.PlanCacheUnqualified(),
}
if sessVars.CacheStmtExecInfo == nil {
sessVars.CacheStmtExecInfo = &stmtsummary.StmtExecInfo{}
}
stmtExecInfo := sessVars.CacheStmtExecInfo
stmtExecInfo.SchemaName = strings.ToLower(sessVars.CurrentDB)
stmtExecInfo.Charset = charset
stmtExecInfo.Collation = collation
stmtExecInfo.NormalizedSQL = normalizedSQL
stmtExecInfo.Digest = digest.String()
stmtExecInfo.PrevSQL = prevSQL
stmtExecInfo.PrevSQLDigest = prevSQLDigest
stmtExecInfo.PlanDigest = planDigest
stmtExecInfo.User = userString
stmtExecInfo.TotalLatency = costTime
stmtExecInfo.ParseLatency = sessVars.DurationParse
stmtExecInfo.CompileLatency = sessVars.DurationCompile
stmtExecInfo.StmtCtx = stmtCtx
stmtExecInfo.CopTasks = copTaskInfo
stmtExecInfo.ExecDetail = execDetail
stmtExecInfo.MemMax = memMax
stmtExecInfo.DiskMax = diskMax
stmtExecInfo.StartTime = sessVars.StartTime
stmtExecInfo.IsInternal = sessVars.InRestrictedSQL
stmtExecInfo.Succeed = succ
stmtExecInfo.PlanInCache = sessVars.FoundInPlanCache
stmtExecInfo.PlanInBinding = sessVars.FoundInBinding
stmtExecInfo.ExecRetryCount = a.retryCount
stmtExecInfo.StmtExecDetails = stmtDetail
stmtExecInfo.ResultRows = resultRows
stmtExecInfo.TiKVExecDetails = tikvExecDetail
stmtExecInfo.Prepared = a.isPreparedStmt
stmtExecInfo.KeyspaceName = keyspaceName
stmtExecInfo.KeyspaceID = keyspaceID
stmtExecInfo.RUDetail = ruDetail
stmtExecInfo.ResourceGroupName = sessVars.StmtCtx.ResourceGroupName
stmtExecInfo.CPUUsages = sessVars.SQLCPUUsages.GetCPUUsages()
stmtExecInfo.PlanCacheUnqualified = sessVars.StmtCtx.PlanCacheUnqualified()
stmtExecInfo.LazyInfo = a
if a.retryCount > 0 {
stmtExecInfo.ExecRetryTime = costTime - sessVars.DurationParse - sessVars.DurationCompile - time.Since(a.retryStartTime)
}
stmtsummaryv2.Add(stmtExecInfo)
}

// GetOriginalSQL implements StmtExecLazyInfo interface.
func (a *ExecStmt) GetOriginalSQL() string {
stmt := a.getLazyStmtText()
return stmt.String()
}

// GetEncodedPlan implements StmtExecLazyInfo interface.
func (a *ExecStmt) GetEncodedPlan() (p string, h string, e any) {
defer func() {
e = recover()
if e != nil {
logutil.BgLogger().Warn("fail to generate plan info",
zap.Stack("backtrace"),
zap.Any("error", e))
}
}()

sessVars := a.Ctx.GetSessionVars()
p, h = getEncodedPlan(sessVars.StmtCtx, !sessVars.InRestrictedSQL)
return
}

// GetBinaryPlan implements StmtExecLazyInfo interface.
func (a *ExecStmt) GetBinaryPlan() string {
if variable.GenerateBinaryPlan.Load() {
return getBinaryPlan(a.Ctx)
}
return ""
}

// GetPlanDigest implements StmtExecLazyInfo interface.
func (a *ExecStmt) GetPlanDigest() string {
if a.Plan.TP() == plancodec.TypePointGet {
_, planDigest := GetPlanDigest(a.Ctx.GetSessionVars().StmtCtx)
return planDigest.String()
}
return ""
}

// GetTextToLog return the query text to log.
func (a *ExecStmt) GetTextToLog(keepHint bool) string {
var sql string
Expand Down
1 change: 1 addition & 0 deletions pkg/sessionctx/variable/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ go_library(
"//pkg/util/servicescope",
"//pkg/util/size",
"//pkg/util/sqlkiller",
"//pkg/util/stmtsummary",
"//pkg/util/stmtsummary/v2:stmtsummary",
"//pkg/util/stringutil",
"//pkg/util/tableutil",
Expand Down
4 changes: 4 additions & 0 deletions pkg/sessionctx/variable/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ import (
"github.com/pingcap/tidb/pkg/util/replayer"
"github.com/pingcap/tidb/pkg/util/rowcodec"
"github.com/pingcap/tidb/pkg/util/sqlkiller"
"github.com/pingcap/tidb/pkg/util/stmtsummary"
"github.com/pingcap/tidb/pkg/util/stringutil"
"github.com/pingcap/tidb/pkg/util/tableutil"
"github.com/pingcap/tidb/pkg/util/tiflash"
Expand Down Expand Up @@ -1711,6 +1712,9 @@ type SessionVars struct {

// ScatterRegion will scatter the regions for DDLs when it is "table" or "global", "" indicates not trigger scatter.
ScatterRegion string

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

// GetSessionVars implements the `SessionVarsProvider` interface.
Expand Down
19 changes: 15 additions & 4 deletions pkg/util/execdetails/execdetails.go
Original file line number Diff line number Diff line change
Expand Up @@ -464,14 +464,14 @@ func (s *SyncExecDetails) GetExecDetails() ExecDetails {
}

// CopTasksDetails returns some useful information of cop-tasks during execution.
func (s *SyncExecDetails) CopTasksDetails() CopTasksDetails {
func (s *SyncExecDetails) CopTasksDetails() *CopTasksDetails {
s.mu.Lock()
defer s.mu.Unlock()
n := s.detailsSummary.NumCopTasks
d := CopTasksDetails{NumCopTasks: n}
if n == 0 {
return d
return nil
}
d := &CopTasksDetails{NumCopTasks: n}
d.TotProcessTime = s.execDetails.TimeDetail.ProcessTime
d.AvgProcessTime = d.TotProcessTime / time.Duration(n)

Expand Down Expand Up @@ -536,7 +536,7 @@ type CopTasksDetails struct {

// ToZapFields wraps the CopTasksDetails as zap.Fileds.
func (d *CopTasksDetails) ToZapFields() (fields []zap.Field) {
if d.NumCopTasks == 0 {
if d == nil || d.NumCopTasks == 0 {
return
}
fields = make([]zap.Field, 0, 10)
Expand Down Expand Up @@ -1539,6 +1539,17 @@ func (e *RuntimeStatsColl) GetRootStats(planID int) *RootRuntimeStats {
return runtimeStats
}

// GetPlanActRows returns the actual rows of the plan.
func (e *RuntimeStatsColl) GetPlanActRows(planID int) int64 {
e.mu.Lock()
defer e.mu.Unlock()
runtimeStats, exists := e.rootStats[planID]
if !exists {
return 0
}
return runtimeStats.GetActRows()
}

// GetCopStats gets the CopRuntimeStats specified by planID.
func (e *RuntimeStatsColl) GetCopStats(planID int) *CopRuntimeStats {
e.mu.Lock()
Expand Down
1 change: 0 additions & 1 deletion pkg/util/stmtsummary/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ go_test(
"//pkg/util/execdetails",
"//pkg/util/plancodec",
"//pkg/util/ppcpuusage",
"//pkg/util/stringutil",
"@com_github_pingcap_log//:log",
"@com_github_stretchr_testify//require",
"@com_github_tikv_client_go_v2//util",
Expand Down
Loading