SQL Parse:对SQL文件进行指纹输出

发布于:2022-11-28 ⋅ 阅读:(753) ⋅ 点赞:(0)

导读

本文主要介绍如何借助 TiDB SQL 解析自定义生成 SQL 指纹,采用了一种有别于 pt-fingerprint(https://www.percona.com/doc/percona-toolkit/3.0/pt-fingerprint.html) 的方式。

介绍:

承接上篇文章:golang mysql 慢日志解析工具_雨丶花丶石的博客-CSDN博客

之前做了一个mysql slowlog的解析工具,那么有时候需要将慢查询进行入库,然后进行分析、或者制作报表、趋势图等;如将慢查询入库后,可以使用SQL指纹的进行统计分析(统计相同指纹语句的在某段时间范围的查询次数、最大查询时间、平均查询时间等)

这时候就需要把对SQL文件进行指纹输出,例如pt-fingerprint工具:

pt-fingerprint[1]工具提供将SQL语句转换为指纹,支持以下的功能:

  • 多值INSERT语句缩减为单个VALUES()列表

  • 删除语句注释

  • 替换可选参数,如带引号的字符串

  • 可以将分库分表名中的数字进行替换

  • 缩减空格的数量,统一格式

  • 将语句全部进行小写,替换IN()和VALUES()中的值

  • 将UNION语句合并为一个语句

那么如何使用go语言去实现了,这里就介绍 如何使用go实现对SQL进行解析指纹输出


功能展示:

这里面使用了tidb 的SQL Parse 解析,参考:tidb/quickstart.md at master · pingcap/tidb · GitHub具体功能展示如下:

代码展示:

之前在go1.14版本中有个exp/constraints包编译报错,后来把go升级到1.18+以上版本就搞定了,原理是exp/constraints 使用了泛型,这种语法在go1.8 才支持。

具体代码如下:

package main

import (
	"bytes"
	"flag"
	"github.com/fatih/color"
	"github.com/pingcap/tidb/parser"
	"github.com/pingcap/tidb/parser/ast"
	"github.com/pingcap/tidb/parser/format"
	driver "github.com/pingcap/tidb/types/parser_driver"
)

// 定义一个 FingerprintVisitor 使其实现 Visitor 接口
type FingerprintVisitor struct{}

func (f *FingerprintVisitor) Enter(n ast.Node) (node ast.Node, skipChildren bool) {
	// 当访问到ValueExpr 时,只需要将ValueExpr的值替换掉就行
	if v, ok := n.(*driver.ValueExpr); ok {
		//v.Type.Charset = ""
		v.SetValue([]byte("?"))
	}
	return n, false
}

func (f *FingerprintVisitor) Leave(n ast.Node) (node ast.Node, ok bool) {
	return n, true
}
var (
	Success *color.Color
	Err *color.Color
	sql string
)
func main() {
	Success=color.New(color.FgHiGreen,color.Bold,color.Underline)
	Err=color.New(color.FgHiRed, color.Bold)
	flag.StringVar(&sql, "sql", "", "SQL语句")
	p := parser.New()
	flag.Parse()
	stmt, err := p.ParseOneStmt(sql, "", "")
	if err != nil {
		// 省略错误处理
		Err.Println("解析错误:"+err.Error())
		return
	}
	stmt.Accept(&FingerprintVisitor{})

	buf := new(bytes.Buffer)
	restoreCtx := format.NewRestoreCtx(format.RestoreKeyWordUppercase|format.RestoreNameBackQuotes, buf)
	err = stmt.Restore(restoreCtx)
	if nil != err {
		// 省略错误处理
		Err.Println("解析错误:"+err.Error())
		return
	}
	Success.Println(buf.String())

}

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到