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  }