上一篇中,提到了使用pt-fingerprint来做sql指纹采集,实际发布到生产环境后,发现有些问题。
1、处理的sql如何用到了反引号,则会报错,例如:
pt-fingerprint --query "SELECT id,`group`,shop_id,org_name,pid,is_show,org_level,root_id,path,agent_num FROM sbt1 WHERE id=19765"
zsh: command not found: group
select id,,shop_id,org_name,pid,is_show,org_level,root_id,path,agent_num from sbt1 where id=?
2、性能不太给力,每次执行需要0.03s到0.04秒
试了下小米的soar,sql指纹采集的性能也不给力。
找了挺久,发现percona这篇博客,使用golang重写的sql指纹采集(除此之外,还带有日志分析的功能)
因为percona提供的是package的方式,如果需要打包成程序,还需要写点代码 ,下面简单记录下编译方法:
假设GOPATH路径为:/home/gocode/
cd /home/gocode
cd src
mkdir github.com
cd github.com
git clone https://github.com/percona/go-mysql.git
cd go-mysql
git checkout master
go mod init
go mod tidy
go mod vendor
make install
# 切换到query目录下
cd query
新建 main.go 文件,内容如下:
package main
import (
"fmt"
"os"
"github.com/percona/go-mysql/query"
)
func main() {
// 仅从命令行参数读取查询语句,如果没有提供,则报错退出
if len(os.Args) <= 1 {
fmt.Println("Error: Query string not provided.")
os.Exit(1)
}
q := os.Args[1]
f := query.Fingerprint(q)
fmt.Println(f)
}
# 测试
go run main.go 'update sbt1 set sex ="M" where id between 1 and 100 and age !=12 limit 10 '
# 打包
go build main.go
# 列出文件
# root @ localhost in /home/gocode/src/github.com/go-mysql/query
$ ll
total 1.9M
-rw-r--r-- 1 root root 1.8K 2024-04-28 13:52 doc_test.go
-rw-r--r-- 1 root root 24K 2024-04-28 13:52 query.go
-rw-r--r-- 1 root root 19K 2024-04-28 13:52 query_test.go
-rw-r--r-- 1 root root 367 2024-04-28 13:55 main.go
-rwxr-xr-x 1 root root 1.9M 2024-04-28 13:55 main --> 生成的可执行的二进制文件
# 还可以移动到/bin目录下
cp main /bin/go-pt-fingerprint
import time
import subprocess
sql="""
EXPLAIN SELECT id,tag_id,tag_name,group_id,group_name,agent_id,create_user,create_time,update_user,update_time FROM sbt1
WHERE (tag_id = '1111111111' AND del_flag = 0)
"""
s1=time.time()
for i in range(10000):
cmd = subprocess.Popen(
r"go-pt-fingerprint "
+ '"'
+ str(sql).replace("`", "").replace('"', "'")
+ '"',
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
print("go版本 ",time.time()- s1)
s2=time.time()
for i in range(10000):
cmd = subprocess.Popen(
r"pt-fingerprint --query "
+ '"'
+ str(sql).replace("`", "").replace('"', "'")
+ '"',
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
print("perl版本 ",time.time()- s2)
结论:
go版本 8.751 秒
perl版本 44.137秒
golang版本的pt-fingerprint性能是perl版本的5倍左右。
如果 sql里面有反引号, pt-fingerprint 执行会遇到异常,如下:
$ pt-fingerprint --query "SELECT id,`group`,shop_id,org_name,pid,is_show,org_level,root_id,path,agent_num FROM sbt1 WHERE id=19765"
zsh: command not found: group
select id,,shop_id,org_name,pid,is_show,org_level,root_id,path,agent_num from sbt1 where id=?
经测试,go版本的也存在这种问题
$ ./main "SELECT id,`limit`,shop_id,org_name,pid,is_show,org_level,root_id,path,agent_num FROM sbt1 WHERE name between '2023-02-01' and '2023-05-01' and sex='M' order by id desc limit 100,20 "
select id,cputime unlimited filesize unlimited datasize unlimited stacksize 8mb coredumpsize 0kb memoryuse unlimited maxproc ? descriptors ? memorylocked 8mb addressspace unlimited maxfilelocks unlimited sigpending ? msgqueue ? nice ? rt_priority ? rt_time unlimited,shop_id,org_name,pid,is_show,org_level,root_id,path,agent_num from sbt1 where name between ? and ? and sex=? order by id desc limit ?,?
解决办法:
在使用前,先处理一次,使用replace将反引号去掉
对于sql归一化,业内还有很多工具,例如:
1、tidb sql parser
2、jsqlparser
3、druid ,示例可以参考这篇
考虑到java启动比较慢,推荐使用文本的percona方案或者tidb的方案。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。