github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/pingcap/tidb/perfschema/statement.go (about) 1 // Copyright 2016 PingCAP, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package perfschema 15 16 import ( 17 "fmt" 18 "reflect" 19 "runtime" 20 "sync/atomic" 21 "time" 22 23 "github.com/insionng/yougam/libraries/juju/errors" 24 "github.com/insionng/yougam/libraries/ngaut/log" 25 "github.com/insionng/yougam/libraries/pingcap/tidb/ast" 26 "github.com/insionng/yougam/libraries/pingcap/tidb/util/types" 27 ) 28 29 // statementInfo defines statement instrument information. 30 type statementInfo struct { 31 // The registered statement key 32 key uint64 33 // The name of the statement instrument to register 34 name string 35 } 36 37 // StatementState provides temporary storage to a statement runtime statistics. 38 // TODO: 39 // 1. support statement digest. 40 // 2. support prepared statement. 41 type StatementState struct { 42 // Connection identifier 43 connID uint64 44 // Statement information 45 info *statementInfo 46 // Statement type 47 stmtType reflect.Type 48 // Source file and line number 49 source string 50 // Timer name 51 timerName enumTimerName 52 // Timer start 53 timerStart int64 54 // Timer end 55 timerEnd int64 56 // Locked time 57 lockTime int64 58 // SQL statement string 59 sqlText string 60 // Current schema name 61 schemaName string 62 // Number of errors 63 errNum uint32 64 // Number of warnings 65 warnNum uint32 66 // Rows affected 67 rowsAffected uint64 68 // Rows sent 69 rowsSent uint64 70 // Rows examined 71 rowsExamined uint64 72 // Metric, temporary tables created on disk 73 createdTmpDiskTables uint32 74 // Metric, temproray tables created 75 createdTmpTables uint32 76 // Metric, number of select full join 77 selectFullJoin uint32 78 // Metric, number of select full range join 79 selectFullRangeJoin uint32 80 // Metric, number of select range 81 selectRange uint32 82 // Metric, number of select range check 83 selectRangeCheck uint32 84 // Metric, number of select scan 85 selectScan uint32 86 // Metric, number of sort merge passes 87 sortMergePasses uint32 88 // Metric, number of sort merge 89 sortRange uint32 90 // Metric, number of sort rows 91 sortRows uint32 92 // Metric, number of sort scans 93 sortScan uint32 94 // Metric, no index used flag 95 noIndexUsed uint8 96 // Metric, no good index used flag 97 noGoodIndexUsed uint8 98 } 99 100 func (ps *perfSchema) RegisterStatement(category, name string, elem interface{}) { 101 instrumentName := fmt.Sprintf("%s%s/%s", statementInstrumentPrefix, category, name) 102 key, err := ps.addInstrument(instrumentName) 103 if err != nil { 104 // just ignore, do nothing else. 105 log.Errorf("Unable to register instrument %s", instrumentName) 106 return 107 } 108 109 ps.stmtInfos[reflect.TypeOf(elem)] = &statementInfo{ 110 key: key, 111 name: instrumentName, 112 } 113 } 114 115 func (ps *perfSchema) StartStatement(sql string, connID uint64, callerName EnumCallerName, elem interface{}) *StatementState { 116 stmtType := reflect.TypeOf(elem) 117 info, ok := ps.stmtInfos[stmtType] 118 if !ok { 119 // just ignore, do nothing else. 120 log.Errorf("No instrument registered for statement %s", stmtType) 121 return nil 122 } 123 124 // check and apply the configuration parameter in table setup_timers. 125 timerName, err := ps.getTimerName(flagStatement) 126 if err != nil { 127 // just ignore, do nothing else. 128 log.Error("Unable to check setup_timers table") 129 return nil 130 } 131 var timerStart int64 132 switch timerName { 133 case timerNameNanosec: 134 timerStart = time.Now().UnixNano() 135 case timerNameMicrosec: 136 timerStart = time.Now().UnixNano() / int64(time.Microsecond) 137 case timerNameMillisec: 138 timerStart = time.Now().UnixNano() / int64(time.Millisecond) 139 default: 140 return nil 141 } 142 143 // TODO: check and apply the additional configuration parameters in: 144 // - table setup_actors 145 // - table setup_setup_consumers 146 // - table setup_instruments 147 // - table setup_objects 148 149 var source string 150 callerLock.RLock() 151 source, ok = callerNames[callerName] 152 callerLock.RUnlock() 153 if !ok { 154 _, fileName, fileLine, ok := runtime.Caller(1) 155 if !ok { 156 // just ignore, do nothing else. 157 log.Error("Unable to get runtime.Caller(1)") 158 return nil 159 } 160 source = fmt.Sprintf("%s:%d", fileName, fileLine) 161 162 callerLock.Lock() 163 callerNames[callerName] = source 164 callerLock.Unlock() 165 } 166 167 return &StatementState{ 168 connID: connID, 169 info: info, 170 stmtType: stmtType, 171 source: source, 172 timerName: timerName, 173 timerStart: timerStart, 174 sqlText: sql, 175 } 176 } 177 178 func (ps *perfSchema) EndStatement(state *StatementState) { 179 if state == nil { 180 return 181 } 182 183 switch state.timerName { 184 case timerNameNanosec: 185 state.timerEnd = time.Now().UnixNano() 186 case timerNameMicrosec: 187 state.timerEnd = time.Now().UnixNano() / int64(time.Microsecond) 188 case timerNameMillisec: 189 state.timerEnd = time.Now().UnixNano() / int64(time.Millisecond) 190 default: 191 return 192 } 193 194 log.Debugf("EndStatement: sql %s, connection id %d, type %s", state.sqlText, state.connID, state.stmtType) 195 196 record := state2Record(state) 197 err := ps.updateEventsStmtsCurrent(state.connID, record) 198 if err != nil { 199 log.Error("Unable to update events_statements_current table") 200 } 201 err = ps.appendEventsStmtsHistory(record) 202 if err != nil { 203 log.Errorf("Unable to append to events_statements_history table %v", errors.ErrorStack(err)) 204 } 205 } 206 207 func state2Record(state *StatementState) []types.Datum { 208 return types.MakeDatums( 209 state.connID, // THREAD_ID 210 state.info.key, // EVENT_ID 211 nil, // END_EVENT_ID 212 state.info.name, // EVENT_NAME 213 state.source, // SOURCE 214 uint64(state.timerStart), // TIMER_START 215 uint64(state.timerEnd), // TIMER_END 216 nil, // TIMER_WAIT 217 uint64(state.lockTime), // LOCK_TIME 218 state.sqlText, // SQL_TEXT 219 nil, // DIGEST 220 nil, // DIGEST_TEXT 221 state.schemaName, // CURRENT_SCHEMA 222 nil, // OBJECT_TYPE 223 nil, // OBJECT_SCHEMA 224 nil, // OBJECT_NAME 225 nil, // OBJECT_INSTANCE_BEGIN 226 nil, // MYSQL_ERRNO, 227 nil, // RETURNED_SQLSTATE 228 nil, // MESSAGE_TEXT 229 uint64(state.errNum), // ERRORS 230 uint64(state.warnNum), // WARNINGS 231 state.rowsAffected, // ROWS_AFFECTED 232 state.rowsSent, // ROWS_SENT 233 state.rowsExamined, // ROWS_EXAMINED 234 uint64(state.createdTmpDiskTables), // CREATED_TMP_DISK_TABLES 235 uint64(state.createdTmpTables), // CREATED_TMP_TABLES 236 uint64(state.selectFullJoin), // SELECT_FULL_JOIN 237 uint64(state.selectFullRangeJoin), // SELECT_FULL_RANGE_JOIN 238 uint64(state.selectRange), // SELECT_RANGE 239 uint64(state.selectRangeCheck), // SELECT_RANGE_CHECK 240 uint64(state.selectScan), // SELECT_SCAN 241 uint64(state.sortMergePasses), // SORT_MERGE_PASSES 242 uint64(state.sortRange), // SORT_RANGE 243 uint64(state.sortRows), // SORT_ROWS 244 uint64(state.sortScan), // SORT_SCAN 245 uint64(state.noIndexUsed), // NO_INDEX_USED 246 uint64(state.noGoodIndexUsed), // NO_GOOD_INDEX_USED 247 nil, // NESTING_EVENT_ID 248 nil, // NESTING_EVENT_TYPE 249 nil, // NESTING_EVENT_LEVEL 250 ) 251 } 252 253 func (ps *perfSchema) updateEventsStmtsCurrent(connID uint64, record []types.Datum) error { 254 tbl := ps.mTables[TableStmtsCurrent] 255 if tbl == nil { 256 return nil 257 } 258 index := connID % uint64(currentElemMax) 259 handle := atomic.LoadInt64(&ps.stmtHandles[index]) 260 if handle == 0 { 261 newHandle, err := tbl.AddRecord(nil, record) 262 if err != nil { 263 return errors.Trace(err) 264 } 265 atomic.StoreInt64(&ps.stmtHandles[index], newHandle) 266 return nil 267 } 268 err := tbl.UpdateRecord(nil, handle, nil, record, nil) 269 if err != nil { 270 return errors.Trace(err) 271 } 272 return nil 273 } 274 275 func (ps *perfSchema) appendEventsStmtsHistory(record []types.Datum) error { 276 tbl := ps.mTables[TableStmtsHistory] 277 if tbl == nil { 278 return nil 279 } 280 _, err := tbl.AddRecord(nil, record) 281 if err != nil { 282 return errors.Trace(err) 283 } 284 return nil 285 } 286 287 func (ps *perfSchema) registerStatements() { 288 ps.stmtInfos = make(map[reflect.Type]*statementInfo) 289 // Existing instrument names are the same as MySQL 5.7 290 ps.RegisterStatement("sql", "alter_table", (*ast.AlterTableStmt)(nil)) 291 ps.RegisterStatement("sql", "begin", (*ast.BeginStmt)(nil)) 292 ps.RegisterStatement("sql", "commit", (*ast.CommitStmt)(nil)) 293 ps.RegisterStatement("sql", "create_db", (*ast.CreateDatabaseStmt)(nil)) 294 ps.RegisterStatement("sql", "create_index", (*ast.CreateIndexStmt)(nil)) 295 ps.RegisterStatement("sql", "create_table", (*ast.CreateTableStmt)(nil)) 296 ps.RegisterStatement("sql", "deallocate", (*ast.DeallocateStmt)(nil)) 297 ps.RegisterStatement("sql", "delete", (*ast.DeleteStmt)(nil)) 298 ps.RegisterStatement("sql", "do", (*ast.DoStmt)(nil)) 299 ps.RegisterStatement("sql", "drop_db", (*ast.DropDatabaseStmt)(nil)) 300 ps.RegisterStatement("sql", "drop_table", (*ast.DropTableStmt)(nil)) 301 ps.RegisterStatement("sql", "drop_index", (*ast.DropIndexStmt)(nil)) 302 ps.RegisterStatement("sql", "execute", (*ast.ExecuteStmt)(nil)) 303 ps.RegisterStatement("sql", "explain", (*ast.ExplainStmt)(nil)) 304 ps.RegisterStatement("sql", "insert", (*ast.InsertStmt)(nil)) 305 ps.RegisterStatement("sql", "prepare", (*ast.PrepareStmt)(nil)) 306 ps.RegisterStatement("sql", "rollback", (*ast.RollbackStmt)(nil)) 307 ps.RegisterStatement("sql", "select", (*ast.SelectStmt)(nil)) 308 ps.RegisterStatement("sql", "set", (*ast.SetStmt)(nil)) 309 ps.RegisterStatement("sql", "show", (*ast.ShowStmt)(nil)) 310 ps.RegisterStatement("sql", "truncate", (*ast.TruncateTableStmt)(nil)) 311 ps.RegisterStatement("sql", "union", (*ast.UnionStmt)(nil)) 312 ps.RegisterStatement("sql", "update", (*ast.UpdateStmt)(nil)) 313 ps.RegisterStatement("sql", "use", (*ast.UseStmt)(nil)) 314 }