profiling.go 3.1 KB
Newer Older
martianzhang's avatar
martianzhang 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
/*
 * Copyright 2018 Xiaomi, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package database

import (
	"errors"
	"fmt"
	"strings"

	"github.com/XiaoMi/soar/common"

	"vitess.io/vitess/go/vt/sqlparser"
)

// Profiling show profile输出的结果
type Profiling struct {
	Rows []ProfilingRow
}

// ProfilingRow show profile每一行信息
type ProfilingRow struct {
	Status   string
	Duration float64
	// TODO: 支持show profile all,不过目前看all的信息过多有点眼花缭乱
}

41 42 43
// Profiling 执行SQL,并对其 Profile
func (db *Connector) Profiling(sql string, params ...interface{}) ([]ProfilingRow, error) {
	var rows []ProfilingRow
martianzhang's avatar
martianzhang 已提交
44
	// 过滤不需要 profiling 的 SQL
martianzhang's avatar
martianzhang 已提交
45 46 47
	switch sqlparser.Preview(sql) {
	case sqlparser.StmtSelect, sqlparser.StmtUpdate, sqlparser.StmtDelete:
	default:
48
		return rows, errors.New("no need profiling")
martianzhang's avatar
martianzhang 已提交
49 50 51 52
	}

	// 测试环境如果检查是关闭的,则SQL不会被执行
	if common.Config.TestDSN.Disable {
53
		return rows, errors.New("dsn is disable")
martianzhang's avatar
martianzhang 已提交
54 55
	}

martianzhang's avatar
martianzhang 已提交
56
	// 数据库安全性检查:如果 Connector 的 IP 端口与 TEST 环境不一致,则启用 SQL 白名单
57
	// 不在白名单中的 SQL 不允许执行
martianzhang's avatar
martianzhang 已提交
58 59
	// 执行环境与test环境不相同
	if db.Addr != common.Config.TestDSN.Addr && db.dangerousQuery(sql) {
60
		return rows, fmt.Errorf("query execution deny: Execute SQL with DSN(%s/%s) '%s'",
martianzhang's avatar
martianzhang 已提交
61 62 63 64
			db.Addr, db.Database, fmt.Sprintf(sql, params...))
	}

	common.Log.Debug("Execute SQL with DSN(%s/%s) : %s", db.Addr, db.Database, sql)
65 66
	// Keep connection
	// https://github.com/go-sql-driver/mysql/issues/208
martianzhang's avatar
martianzhang 已提交
67
	trx, err := db.Conn.Begin()
martianzhang's avatar
martianzhang 已提交
68
	if err != nil {
69
		return rows, err
martianzhang's avatar
martianzhang 已提交
70
	}
71 72 73 74 75
	defer trx.Rollback()

	// 开启 Profiling
	_, err = trx.Query("set @@profiling=1")
	common.LogIfError(err, "")
martianzhang's avatar
martianzhang 已提交
76

77 78 79 80 81 82 83 84
	// 执行 SQL,抛弃返回结果
	tmpRes, err := trx.Query(sql, params...)
	if err != nil {
		return rows, err
	}
	for tmpRes.Next() {
		continue
	}
martianzhang's avatar
martianzhang 已提交
85

86 87
	// 返回 Profiling 结果
	res, err := trx.Query("show profile")
martianzhang's avatar
martianzhang 已提交
88 89 90 91 92
	if err != nil {
		trx.Rollback()
		return rows, err
	}
	var profileRow ProfilingRow
93
	for res.Next() {
martianzhang's avatar
martianzhang 已提交
94
		err = res.Scan(&profileRow.Status, &profileRow.Duration)
martianzhang's avatar
martianzhang 已提交
95
		if err != nil {
96
			common.LogIfError(err, "")
martianzhang's avatar
martianzhang 已提交
97
			break
martianzhang's avatar
martianzhang 已提交
98
		}
99
		rows = append(rows, profileRow)
martianzhang's avatar
martianzhang 已提交
100
	}
martianzhang's avatar
martianzhang 已提交
101
	res.Close()
martianzhang's avatar
martianzhang 已提交
102

103 104 105 106
	// 关闭 Profiling
	_, err = trx.Query("set @@profiling=0")
	common.LogIfError(err, "")
	return rows, err
martianzhang's avatar
martianzhang 已提交
107 108 109
}

// FormatProfiling 格式化输出Profiling信息
110
func FormatProfiling(rows []ProfilingRow) string {
martianzhang's avatar
martianzhang 已提交
111 112
	str := []string{"| Status | Duration |"}
	str = append(str, "| --- | --- |")
113
	for _, row := range rows {
martianzhang's avatar
martianzhang 已提交
114 115 116 117
		str = append(str, fmt.Sprintf("| %s | %f |", row.Status, row.Duration))
	}
	return strings.Join(str, "\n")
}