diff --git a/config/config.go b/config/config.go index 0417cd65ac760..d30e5796a6b33 100644 --- a/config/config.go +++ b/config/config.go @@ -146,6 +146,7 @@ type Status struct { StatusPort uint `toml:"status-port" json:"status-port"` MetricsAddr string `toml:"metrics-addr" json:"metrics-addr"` MetricsInterval uint `toml:"metrics-interval" json:"metrics-interval"` + RecordQPSbyDB bool `toml:"record-db-qps" json:"record-db-qps"` } // Performance is the performance section of the config. @@ -299,6 +300,7 @@ var defaultConf = Config{ ReportStatus: true, StatusPort: 10080, MetricsInterval: 15, + RecordQPSbyDB: false, }, Performance: Performance{ MaxMemory: 0, diff --git a/config/config.toml.example b/config/config.toml.example index e94f949382735..47895d3efa783 100644 --- a/config/config.toml.example +++ b/config/config.toml.example @@ -119,6 +119,9 @@ metrics-addr = "" # Prometheus client push interval in second, set \"0\" to disable prometheus push. metrics-interval = 15 +# Record statements qps by database name if it is enabled. +record-db-qps = false + [performance] # Max CPUs to use, 0 use number of CPUs in the machine. max-procs = 0 diff --git a/executor/compiler.go b/executor/compiler.go index b94efbaf2e3ba..e263fbffbfcd7 100644 --- a/executor/compiler.go +++ b/executor/compiler.go @@ -122,7 +122,114 @@ func CountStmtNode(stmtNode ast.StmtNode, inRestrictedSQL bool) { if inRestrictedSQL { return } - metrics.StmtNodeCounter.WithLabelValues(GetStmtLabel(stmtNode)).Inc() + + typeLabel := GetStmtLabel(stmtNode) + metrics.StmtNodeCounter.WithLabelValues(typeLabel).Inc() + + if !config.GetGlobalConfig().Status.RecordQPSbyDB { + return + } + + dbLabels := getStmtDbLabel(stmtNode) + for dbLabel := range dbLabels { + metrics.DbStmtNodeCounter.WithLabelValues(dbLabel, typeLabel).Inc() + } +} + +func getStmtDbLabel(stmtNode ast.StmtNode) map[string]struct{} { + dbLabelSet := make(map[string]struct{}) + + switch x := stmtNode.(type) { + case *ast.AlterTableStmt: + dbLabel := x.Table.Schema.O + dbLabelSet[dbLabel] = struct{}{} + case *ast.CreateIndexStmt: + dbLabel := x.Table.Schema.O + dbLabelSet[dbLabel] = struct{}{} + case *ast.CreateTableStmt: + dbLabel := x.Table.Schema.O + dbLabelSet[dbLabel] = struct{}{} + case *ast.InsertStmt: + dbLabels := getDbFromResultNode(x.Table.TableRefs) + for _, db := range dbLabels { + dbLabelSet[db] = struct{}{} + } + dbLabels = getDbFromResultNode(x.Select) + for _, db := range dbLabels { + dbLabelSet[db] = struct{}{} + } + case *ast.DropIndexStmt: + dbLabel := x.Table.Schema.O + dbLabelSet[dbLabel] = struct{}{} + case *ast.DropTableStmt: + tables := x.Tables + for _, table := range tables { + dbLabel := table.Schema.O + if _, ok := dbLabelSet[dbLabel]; !ok { + dbLabelSet[dbLabel] = struct{}{} + } + } + case *ast.SelectStmt: + dbLabels := getDbFromResultNode(x) + for _, db := range dbLabels { + dbLabelSet[db] = struct{}{} + } + case *ast.UpdateStmt: + if x.TableRefs != nil { + dbLabels := getDbFromResultNode(x.TableRefs.TableRefs) + for _, db := range dbLabels { + dbLabelSet[db] = struct{}{} + } + } + case *ast.DeleteStmt: + if x.TableRefs != nil { + dbLabels := getDbFromResultNode(x.TableRefs.TableRefs) + for _, db := range dbLabels { + dbLabelSet[db] = struct{}{} + } + } + } + + return dbLabelSet +} + +func getDbFromResultNode(resultNode ast.ResultSetNode) []string { //may have duplicate db name + var dbLabels []string + + if resultNode == nil { + return dbLabels + } + + switch x := resultNode.(type) { + case *ast.TableSource: + return getDbFromResultNode(x.Source) + case *ast.SelectStmt: + if x.From != nil { + return getDbFromResultNode(x.From.TableRefs) + } + case *ast.TableName: + dbLabels = append(dbLabels, x.DBInfo.Name.O) + case *ast.Join: + if x.Left != nil { + dbs := getDbFromResultNode(x.Left) + if dbs != nil { + for _, db := range dbs { + dbLabels = append(dbLabels, db) + } + } + } + + if x.Right != nil { + dbs := getDbFromResultNode(x.Right) + if dbs != nil { + for _, db := range dbs { + dbLabels = append(dbLabels, db) + } + } + } + } + + return dbLabels } // GetStmtLabel generates a label for a statement. diff --git a/metrics/executor.go b/metrics/executor.go index 9d8936aa64224..57b6d2afdabcf 100644 --- a/metrics/executor.go +++ b/metrics/executor.go @@ -36,4 +36,13 @@ var ( Name: "statement_total", Help: "Counter of StmtNode.", }, []string{LblType}) + + // DbStmtNodeCounter records the number of statement with the same type and db. + DbStmtNodeCounter = prometheus.NewCounterVec( + prometheus.CounterOpts{ + Namespace: "tidb", + Subsystem: "executor", + Name: "statement_db_total", + Help: "Counter of StmtNode by Database.", + }, []string{LblDb, LblType}) ) diff --git a/metrics/metrics.go b/metrics/metrics.go index f1ead074c3711..24a1788fb3fbc 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -99,6 +99,7 @@ func RegisterMetrics() { prometheus.MustRegister(StatementPerTransaction) prometheus.MustRegister(StatsInaccuracyRate) prometheus.MustRegister(StmtNodeCounter) + prometheus.MustRegister(DbStmtNodeCounter) prometheus.MustRegister(StoreQueryFeedbackCounter) prometheus.MustRegister(TiKVBackoffCounter) prometheus.MustRegister(TiKVBackoffHistogram) diff --git a/metrics/session.go b/metrics/session.go index 4bfc98f486f37..07e31d5396fac 100644 --- a/metrics/session.go +++ b/metrics/session.go @@ -106,6 +106,7 @@ const ( LblError = "error" LblRollback = "rollback" LblType = "type" + LblDb = "db" LblResult = "result" LblSQLType = "sql_type" LblGeneral = "general"