Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
Xiaomi
soar
提交
5b5d1604
S
soar
项目概览
Xiaomi
/
soar
大约 1 年 前同步成功
通知
393
Star
8512
Fork
1328
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
S
soar
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
5b5d1604
编写于
11月 01, 2018
作者:
martianzhang
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
use markdownlint format doc
上级
bdb34724
变更
3
显示空白变更内容
内联
并排
Showing
3 changed file
with
71 addition
and
61 deletion
+71
-61
cmd/soar/soar.go
cmd/soar/soar.go
+49
-49
doc/FAQ.md
doc/FAQ.md
+22
-12
doc/images/windows_quote.png
doc/images/windows_quote.png
+0
-0
未找到文件。
cmd/soar/soar.go
浏览文件 @
5b5d1604
...
...
@@ -38,18 +38,18 @@ import (
func
main
()
{
// 全局变量
var
sql
string
// 单条评审指定的
sql或
explain
var
sql
string
// 单条评审指定的
sql 或
explain
sqlCounter
:=
1
// SQL 计数器
lineCounter
:=
1
// 行计数器
var
alterSqls
[]
string
// 待评审的
SQL中所有ALTER
请求
alterTableTimes
:=
make
(
map
[
string
]
int
)
// 待评审的
SQL中同一经表ALTER
请求计数器
suggestMerged
:=
make
(
map
[
string
]
map
[
string
]
advisor
.
Rule
)
// 优化建议去重,
key为sql的
fingerprint.ID
var
alterSqls
[]
string
// 待评审的
SQL 中所有 ALTER
请求
alterTableTimes
:=
make
(
map
[
string
]
int
)
// 待评审的
SQL 中同一经表 ALTER
请求计数器
suggestMerged
:=
make
(
map
[
string
]
map
[
string
]
advisor
.
Rule
)
// 优化建议去重,
key 为 sql 的
fingerprint.ID
ex
,
err
:=
os
.
Executable
()
if
err
!=
nil
{
panic
(
err
)
}
common
.
BaseDir
=
filepath
.
Dir
(
ex
)
// binary文件所在路径
common
.
BaseDir
=
filepath
.
Dir
(
ex
)
// binary
文件所在路径
// 配置文件&命令行参数解析
err
=
common
.
ParseConfig
(
""
)
...
...
@@ -61,17 +61,17 @@ func main() {
advisor
.
ListHeuristicRules
(
advisor
.
HeuristicRules
)
return
}
// 打印支持的
SQL
重写规则
// 打印支持的
SQL
重写规则
if
common
.
Config
.
ListRewriteRules
{
ast
.
ListRewriteRules
(
ast
.
RewriteRules
)
return
}
// 打印所有的测试SQL
// 打印所有的测试
SQL
if
common
.
Config
.
ListTestSqls
{
advisor
.
ListTestSQLs
()
return
}
// 打印支持的report-type
// 打印支持的
report-type
if
common
.
Config
.
ListReportTypes
{
common
.
ListReportTypes
()
return
...
...
@@ -97,7 +97,7 @@ func main() {
return
}
// 读入待优化
SQL,当配置文件或命令行参数未指定SQL
时从管道读取
// 读入待优化
SQL ,当配置文件或命令行参数未指定 SQL
时从管道读取
if
common
.
Config
.
Query
==
""
{
var
data
[]
byte
data
,
err
=
ioutil
.
ReadAll
(
os
.
Stdin
)
...
...
@@ -123,7 +123,7 @@ func main() {
switch
common
.
Config
.
ReportType
{
case
"html"
:
// HTML
格式输入CSS
加载
// HTML
格式输入 CSS
加载
fmt
.
Println
(
common
.
MarkdownHTMLHeader
())
case
"md2html"
:
// markdown2html 转换小工具
...
...
@@ -131,8 +131,8 @@ func main() {
fmt
.
Println
(
common
.
Markdown2HTML
(
sql
))
return
case
"explain-digest"
:
// 当用户输入为
EXPLAIN信息,只对Explain
信息进行分析
// 注意: 这里只能处理一条
SQL的EXPLAIN信息,用户一次反馈多条SQL的EXPLAIN
信息无法处理
// 当用户输入为
EXPLAIN信 息,只对 Explain
信息进行分析
// 注意: 这里只能处理一条
SQL 的 EXPLAIN 信息,用户一次反馈多条 SQL 的 EXPLAIN
信息无法处理
advisor
.
DigestExplainText
(
sql
)
return
case
"remove-comment"
:
...
...
@@ -146,11 +146,11 @@ func main() {
for
;
;
sqlCounter
++
{
var
id
string
// fingerprint.ID
heuristicSuggest
:=
make
(
map
[
string
]
advisor
.
Rule
)
// 启发式建议
expSuggest
:=
make
(
map
[
string
]
advisor
.
Rule
)
// EXPLAIN解读
expSuggest
:=
make
(
map
[
string
]
advisor
.
Rule
)
// EXPLAIN
解读
idxSuggest
:=
make
(
map
[
string
]
advisor
.
Rule
)
// 索引建议
proSuggest
:=
make
(
map
[
string
]
advisor
.
Rule
)
// Profiling信息
traceSuggest
:=
make
(
map
[
string
]
advisor
.
Rule
)
// Trace信息
mysqlSuggest
:=
make
(
map
[
string
]
advisor
.
Rule
)
// MySQL
返回的ERROR
信息
proSuggest
:=
make
(
map
[
string
]
advisor
.
Rule
)
// Profiling
信息
traceSuggest
:=
make
(
map
[
string
]
advisor
.
Rule
)
// Trace
信息
mysqlSuggest
:=
make
(
map
[
string
]
advisor
.
Rule
)
// MySQL
返回的 ERROR
信息
if
buf
==
""
{
common
.
Log
.
Debug
(
"buf: %s, sql: %s empty"
,
buf
,
sql
)
...
...
@@ -178,15 +178,15 @@ func main() {
fingerprint
:=
strings
.
TrimSpace
(
query
.
Fingerprint
(
sql
))
switch
common
.
Config
.
ReportType
{
case
"fingerprint"
:
// SQL指纹
// SQL
指纹
fmt
.
Println
(
fingerprint
)
continue
case
"pretty"
:
// SQL美化
// SQL
美化
fmt
.
Println
(
ast
.
Pretty
(
sql
,
"builtin"
)
+
common
.
Config
.
Delimiter
)
continue
case
"compress"
:
// SQL压缩
// SQL
压缩
fmt
.
Println
(
ast
.
Compress
(
sql
)
+
common
.
Config
.
Delimiter
)
continue
case
"ast"
:
...
...
@@ -210,10 +210,10 @@ func main() {
common
.
LogIfWarn
(
err
,
""
)
continue
default
:
// SQL签名
// SQL
签名
id
=
query
.
Id
(
fingerprint
)
// 建议去重,减少评审整个文件耗时
// TODO: 由于 a = 11
和a = '11'的fingerprint相同,这里一旦跳过即无法检查有些建议了,如:
ARG.003
// TODO: 由于 a = 11
和 a = '11' 的 fingerprint 相同,这里一旦跳过即无法检查有些建议了,如:
ARG.003
if
_
,
ok
:=
suggestMerged
[
id
];
ok
{
continue
}
...
...
@@ -230,7 +230,7 @@ func main() {
switch
stmt
.
(
type
)
{
case
*
sqlparser
.
DDL
:
// 因为
vitess的parser对于DDL
语法支持不好,通过在测试环境执行辅助进行语法检查
// 因为
vitess 的 parser 对于 DDL
语法支持不好,通过在测试环境执行辅助进行语法检查
if
common
.
Config
.
OnlySyntaxCheck
&&
vEnv
.
BuildVirtualEnv
(
rEnv
,
sql
)
{
syntaxErr
=
vEnv
.
Error
}
...
...
@@ -248,7 +248,7 @@ func main() {
if
!
common
.
Config
.
DryRun
{
os
.
Exit
(
1
)
}
// vitess 语法检查给出的建议ERR.000
// vitess 语法检查给出的建议
ERR.000
if
common
.
Config
.
TestDSN
.
Disable
{
mysqlSuggest
[
"ERR.000"
]
=
advisor
.
RuleMySQLError
(
"ERR.000"
,
syntaxErr
)
}
...
...
@@ -277,14 +277,14 @@ func main() {
// +++++++++++++++++++++索引优化建议[开始]+++++++++++++++++++++++{
// 如果配置了索引建议过滤规则,不进行索引优化建议
// 在配置文件
ignore-rules中添加 'IDX.*'
即可屏蔽索引优化建议
// 在配置文件
ignore-rules 中添加 'IDX.*'
即可屏蔽索引优化建议
common
.
Log
.
Debug
(
"start of index advisor Query: %s"
,
q
.
Query
)
if
!
advisor
.
IsIgnoreRule
(
"IDX."
)
{
if
vEnv
.
BuildVirtualEnv
(
rEnv
,
q
.
Query
)
{
idxAdvisor
,
err
:=
advisor
.
NewAdvisor
(
vEnv
,
*
rEnv
,
*
q
)
if
err
!=
nil
||
(
idxAdvisor
==
nil
&&
vEnv
.
Error
==
nil
)
{
if
idxAdvisor
==
nil
{
// 如果
SQL是DDL语句,则返回的idxAdvisor为
nil,可以忽略不处理
// 如果
SQL 是 DDL 语句,则返回的 idxAdvisor 为
nil,可以忽略不处理
// TODO alter table add index 语句检查索引是否已经存在
common
.
Log
.
Debug
(
"idxAdvisor by pass Query: %s"
,
q
.
Query
)
}
else
{
...
...
@@ -311,7 +311,7 @@ func main() {
Case
:
sql
,
}
default
:
// vEnv.VEnvBuild
阶段给出的ERROR是
ERR.001
// vEnv.VEnvBuild
阶段给出的 ERROR 是
ERR.001
mysqlSuggest
[
"ERR.001"
]
=
advisor
.
RuleMySQLError
(
"ERR.001"
,
vEnv
.
Error
)
common
.
Log
.
Error
(
"BuildVirtualEnv DDL Execute Error : %v"
,
vEnv
.
Error
)
}
...
...
@@ -324,30 +324,30 @@ func main() {
common
.
Log
.
Debug
(
"end of index advisor Query: %s"
,
q
.
Query
)
// +++++++++++++++++++++索引优化建议[结束]+++++++++++++++++++++++}
// +++++++++++++++++++++EXPLAIN建议[开始]+++++++++++++++++++++++{
// 如果未配置
Online或Test无法给Explain
建议
// +++++++++++++++++++++EXPLAIN
建议[开始]+++++++++++++++++++++++{
// 如果未配置
Online 或 Test 无法给 Explain
建议
common
.
Log
.
Debug
(
"start of explain Query: %s"
,
q
.
Query
)
if
!
common
.
Config
.
OnlineDSN
.
Disable
&&
!
common
.
Config
.
TestDSN
.
Disable
{
// 因为
EXPLAIN
依赖数据库环境,所以把这段逻辑放在启发式建议和索引建议后面
// 因为
EXPLAIN
依赖数据库环境,所以把这段逻辑放在启发式建议和索引建议后面
if
common
.
Config
.
Explain
{
// 执行EXPLAIN
// 执行
EXPLAIN
explainInfo
,
err
:=
rEnv
.
Explain
(
q
.
Query
,
database
.
ExplainType
[
common
.
Config
.
ExplainType
],
database
.
ExplainFormatType
[
common
.
Config
.
ExplainFormat
])
if
err
!=
nil
{
// 线上环境执行失败才到测试环境EXPLAIN,比如在用户提供建表语句及查询语句的场景
// 线上环境执行失败才到测试环境
EXPLAIN,比如在用户提供建表语句及查询语句的场景
common
.
Log
.
Warn
(
"rEnv.Explain Warn: %v"
,
err
)
explainInfo
,
err
=
vEnv
.
Explain
(
q
.
Query
,
database
.
ExplainType
[
common
.
Config
.
ExplainType
],
database
.
ExplainFormatType
[
common
.
Config
.
ExplainFormat
])
if
err
!=
nil
{
// EXPLAIN
阶段给出的ERROR是
ERR.002
// EXPLAIN
阶段给出的 ERROR 是
ERR.002
mysqlSuggest
[
"ERR.002"
]
=
advisor
.
RuleMySQLError
(
"ERR.002"
,
err
)
common
.
Log
.
Error
(
"vEnv.Explain Error: %v"
,
err
)
continue
}
}
// 分析
EXPLAIN
结果
// 分析
EXPLAIN
结果
if
explainInfo
!=
nil
{
expSuggest
=
advisor
.
ExplainAdvisor
(
explainInfo
)
}
else
{
...
...
@@ -356,9 +356,9 @@ func main() {
}
}
common
.
Log
.
Debug
(
"end of explain Query: %s"
,
q
.
Query
)
// +++++++++++++++++++++
EXPLAIN
建议[结束]+++++++++++++++++++++++}
// +++++++++++++++++++++
EXPLAIN
建议[结束]+++++++++++++++++++++++}
// +++++++++++++++++++++
Profiling
[开始]+++++++++++++++++++++++++{
// +++++++++++++++++++++
Profiling
[开始]+++++++++++++++++++++++++{
common
.
Log
.
Debug
(
"start of profiling Query: %s"
,
q
.
Query
)
if
common
.
Config
.
Profiling
{
res
,
err
:=
vEnv
.
Profiling
(
q
.
Query
)
...
...
@@ -373,9 +373,9 @@ func main() {
}
}
common
.
Log
.
Debug
(
"end of profiling Query: %s"
,
q
.
Query
)
// +++++++++++++++++++++
Profiling
[结束]++++++++++++++++++++++++++}
// +++++++++++++++++++++
Profiling
[结束]++++++++++++++++++++++++++}
// +++++++++++++++++++++Trace [开始]+++++++++++++++++++++++++{
// +++++++++++++++++++++
Trace [开始]+++++++++++++++++++++++++{
common
.
Log
.
Debug
(
"start of trace Query: %s"
,
q
.
Query
)
if
common
.
Config
.
Trace
{
res
,
err
:=
vEnv
.
Trace
(
q
.
Query
)
...
...
@@ -392,14 +392,14 @@ func main() {
common
.
Log
.
Debug
(
"end of trace Query: %s"
,
q
.
Query
)
// +++++++++++++++++++++Trace [结束]++++++++++++++++++++++++++}
// +++++++++++++++++++++SQL重写[开始]+++++++++++++++++++++++++{
// +++++++++++++++++++++SQL
重写[开始]+++++++++++++++++++++++++{
common
.
Log
.
Debug
(
"start of rewrite Query: %s"
,
q
.
Query
)
if
common
.
Config
.
ReportType
==
"rewrite"
{
if
strings
.
HasPrefix
(
strings
.
TrimSpace
(
strings
.
ToLower
(
sql
)),
"create"
)
||
strings
.
HasPrefix
(
strings
.
TrimSpace
(
strings
.
ToLower
(
sql
)),
"alter"
)
||
strings
.
HasPrefix
(
strings
.
TrimSpace
(
strings
.
ToLower
(
sql
)),
"rename"
)
{
// 依赖上下文件的
SQL重写,如:多条ALTER SQL
合并
// vitess
对DDL语法的支持不好,大部分DDL会语法解析出错,但即使出错了还是会生成一个stmt而且里面的db.table
还是准确的。
// 依赖上下文件的
SQL 重写,如:多条 ALTER SQL
合并
// vitess
对 DDL 语法的支持不好,大部分 DDL 会语法解析出错,但即使出错了还是会生成一个 stmt 而且里面的 db.table
还是准确的。
alterSqls
=
append
(
alterSqls
,
sql
)
alterTbl
:=
ast
.
AlterAffectTable
(
stmt
)
...
...
@@ -412,25 +412,25 @@ func main() {
}
}
}
else
{
// 其他不依赖上下文件的
SQL
重写
// 其他不依赖上下文件的
SQL
重写
rw
:=
ast
.
NewRewrite
(
sql
)
if
rw
==
nil
{
// 都到这一步了
sql不会语法不正确,因此rw一般不会为
nil
// 都到这一步了
sql 不会语法不正确,因此 rw 一般不会为
nil
common
.
Log
.
Critical
(
"NewRewrite nil point error, SQL: %s"
,
sql
)
os
.
Exit
(
1
)
}
// SQL转写需要的源信息采集,如果没有配置环境则只做有限改写
// SQL
转写需要的源信息采集,如果没有配置环境则只做有限改写
meta
:=
ast
.
GetMeta
(
rw
.
Stmt
,
nil
)
rw
.
Columns
=
vEnv
.
GenTableColumns
(
meta
)
// 执行定义好的
SQL
重写规则
// 执行定义好的
SQL
重写规则
rw
.
Rewrite
()
fmt
.
Println
(
strings
.
TrimSpace
(
rw
.
NewSQL
))
}
}
common
.
Log
.
Debug
(
"end of rewrite Query: %s"
,
q
.
Query
)
// +++++++++++++++++++++
SQL
重写[结束]++++++++++++++++++++++++++}
// +++++++++++++++++++++
SQL
重写[结束]++++++++++++++++++++++++++}
// +++++++++++++++++++++打印单条
SQL
优化建议[开始]++++++++++++++++++++++++++{
// +++++++++++++++++++++打印单条
SQL
优化建议[开始]++++++++++++++++++++++++++{
common
.
Log
.
Debug
(
"start of print suggestions, Query: %s"
,
q
.
Query
)
sug
,
str
:=
advisor
.
FormatSuggest
(
q
.
Query
,
common
.
Config
.
ReportType
,
heuristicSuggest
,
idxSuggest
,
expSuggest
,
proSuggest
,
traceSuggest
,
mysqlSuggest
)
suggestMerged
[
id
]
=
sug
...
...
@@ -462,10 +462,10 @@ func main() {
fmt
.
Println
(
str
)
}
common
.
Log
.
Debug
(
"end of print suggestions, Query: %s"
,
q
.
Query
)
// +++++++++++++++++++++打印单条
SQL
优化建议[结束]++++++++++++++++++++++++++}
// +++++++++++++++++++++打印单条
SQL
优化建议[结束]++++++++++++++++++++++++++}
}
// 同一张表的多条
ALTER
语句合并为一条
// 同一张表的多条
ALTER
语句合并为一条
if
ast
.
RewriteRuleMatch
(
"mergealter"
)
{
for
_
,
v
:=
range
ast
.
MergeAlterTables
(
alterSqls
...
)
{
fmt
.
Println
(
strings
.
TrimSpace
(
v
))
...
...
@@ -473,7 +473,7 @@ func main() {
return
}
// 以
JSON
格式化输出
// 以
JSON
格式化输出
if
common
.
Config
.
ReportType
==
"json"
{
js
,
err
:=
json
.
MarshalIndent
(
suggestMerged
,
""
,
" "
)
if
err
==
nil
{
...
...
doc/FAQ.md
浏览文件 @
5b5d1604
#
#
常见问题
# 常见问题
##
#
软件依赖
## 软件依赖
*
[
git
](
https://git-scm.co
)
项目代码管理工具
*
[
go
](
https://golang.org/
)
源码编译依赖
*
[
govendor
](
https://github.com/kardianos/govendor
)
管理第三方包
*
[
docker
](
https://www.docker.com
)
主要用于构建测试环境
*
[
mysql
](
https://www.mysql.com/
)
测试时用来连接测试环境
*
[
retool
](
https://github.com/twitchtv/retool
)
: 管理测试开发工具,首次安装耗时会比较长,如:
`gometalinter.v2`
,
`revive`
,
`golangci-lint`
*
[
retool
](
https://github.com/twitchtv/retool
)
: 管理测试开发工具,首次安装耗时会比较长,如:
`gometalinter.v2`
,
`revive`
,
`golangci-lint`
##
# 命令行参数`test-dsn`, `online-dsn`
中包含特殊字符怎么办?
##
命令行参数 `test-dsn`, `online-dsn`
中包含特殊字符怎么办?
如果
`test-dsn`
或
`online-dsn`
中包含':', '@', '/', '!'等特殊字符建议在配置文件中配置相关信息,配置文件为YAML格式,需要遵守YAML格式的要求规范。
如果
`test-dsn`
或
`online-dsn`
中包含':', '@', '/', '!'等特殊字符建议在配置文件中配置相关信息,配置文件为YAML格式,需要遵守YAML格式的要求规范。
##
#
Windows环境下双击`soar.windows-amd64`文件无反应。
## Windows环境下双击`soar.windows-amd64`文件无反应。
`soar`
是命令行工具,不是图形化桌面工具,Windows环境需要在
`cmd.exe`
下以命令行方式运行。使用
`soar`
前您需要先熟悉Windows命令行使用。
`soar`
是命令行工具,不是图形化桌面工具,Windows环境需要在
`cmd.exe`
下以命令行方式运行。使用
`soar`
前您需要先熟悉Windows命令行使用。
### 提示语法错误
## Windows 用户的引号问题
![
windows_quote
](
https://raw.githubusercontent.com/XiaoMi/soar/master/doc/images/windows_quote.png
)
从上图可以看出 Windows 环境下引号(单引号或双引号)也成为了SQL的一部分传递给 soar 进行分析。因此 Windows 环境下使用如下方式读取 SQL 来解决 Windows 的引号问题。
```
bash
type
query.sql | soar.windows-amd64
```
## 提示语法错误
*
请检查SQL语句中是否出现了不配对的引号,如
`, ", '
##
#
输出结果返回慢
## 输出结果返回慢
* 如果配置了
online-dsn或test-dsn SOAR
会请求这些数据库以支持更多的功能,这时评审一条SQL就会耗时变长。
* 如果又开启了
`
-sampling=true
`
的话会将线上的数据导入到测试环境,数据采样也会消耗一些时间。
* 如果配置了
online-dsn 或 test-dsn SOAR
会请求这些数据库以支持更多的功能,这时评审一条SQL就会耗时变长。
* 如果又开启了
`
-sampling=true
`
的话会将线上的数据导入到测试环境,数据采样也会消耗一些时间。
## 如何搭建测试环境
...
...
@@ -47,7 +57,7 @@ GRANT ALL ON *.* TO root@'hostname';
如属更新vitess仓库可以使用如下命令。
```bash
$
make vitess
make vitess
```
## 生成报告并发邮件
...
...
doc/images/windows_quote.png
0 → 100644
浏览文件 @
5b5d1604
36.8 KB
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录