提交 461b6899 编写于 作者: martianzhang's avatar martianzhang

extract config check into initConfig

上级 d622a265
......@@ -58,7 +58,7 @@ func checkExplainSelectType(exp *database.ExplainInfo) {
if exp.ExplainFormat == database.JSONFormatExplain {
// TODO
// JSON形式遍历分析不方便,转成Row格式也没有SelectType暂不处理
// JSON 形式遍历分析不方便,转成 Row 格式也没有 SelectType 暂不处理
return
}
for _, v := range common.Config.ExplainWarnSelectType {
......
......@@ -1446,7 +1446,7 @@ func (q *Query4Audit) RuleSubqueryDepth() Rule {
}
// RuleSubQueryLimit SUB.005
// 只有IN的SUBQUERY限制了LIMIT,FROM子句中的SUBQUERY并未限制LIMIT
// 只有 IN 的 SUBQUERY 限制了 LIMIT, FROM 子句中的 SUBQUERY 并未限制 LIMIT
func (q *Query4Audit) RuleSubQueryLimit() Rule {
var rule = q.RuleOK()
err := sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
......@@ -1890,7 +1890,7 @@ func (idxAdv *IndexAdvisor) RuleUpdatePrimaryKey() Rule {
err := sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
switch node.(type) {
case *sqlparser.UpdateExpr:
// 获取set操作的全部column
// 获取 set 操作的全部 column
setColumns = append(setColumns, ast.FindAllCols(node)...)
}
return true, nil
......@@ -2820,7 +2820,7 @@ func (q *Query4Audit) RuleIntPrecision() Rule {
switch col.Tp.Tp {
case mysql.TypeLong:
if (col.Tp.Flen < 10 || col.Tp.Flen > 11) && col.Tp.Flen > 0 {
// 有些语言ORM框架会生成int(11),有些语言的框架生成int(10)
// 有些语言 ORM 框架会生成 int(11),有些语言的框架生成 int(10)
rule = HeuristicRules["COL.016"]
break
}
......@@ -2840,7 +2840,7 @@ func (q *Query4Audit) RuleIntPrecision() Rule {
switch col.Tp.Tp {
case mysql.TypeLong:
if (col.Tp.Flen < 10 || col.Tp.Flen > 11) && col.Tp.Flen > 0 {
// 有些语言ORM框架会生成int(11),有些语言的框架生成int(10)
// 有些语言 ORM 框架会生成 int(11),有些语言的框架生成 int(10)
rule = HeuristicRules["COL.016"]
break
}
......
......@@ -48,7 +48,7 @@ type IndexInfo struct {
Name string `json:"name"` // 索引名称
Database string `json:"database"` // 数据库名
Table string `json:"table"` // 表名
DDL string `json:"ddl"` // ALTER, CREATE等类型的DDL语句
DDL string `json:"ddl"` // ALTER, CREATE 等类型的 DDL 语句
ColumnDetails []*common.Column `json:"column_details"` // 列详情
}
......@@ -96,7 +96,7 @@ func NewAdvisor(env *env.VirtualEnv, rEnv database.Connector, q Query4Audit) (*I
dbRef = rEnv.Database
}
// DDL在Env初始化的时候已经执行过了
// DDL 在 Env 初始化的时候已经执行过了
if _, ok := env.TableMap[dbRef]; !ok {
env.TableMap[dbRef] = make(map[string]string)
}
......@@ -279,15 +279,15 @@ func (idxAdv *IndexAdvisor) IndexAdvise() IndexAdvises {
if len(idxAdv.whereINEQ) > 0 {
mergeIndex(indexList, idxAdv.whereINEQ[0])
}
// 有WHERE条件,但WHERE条件未能给出索引建议就不能再加GROUP BY和ORDER BY建议了
// 有WHERE条件,但 WHERE 条件未能给出索引建议就不能再加 GROUP BY 和 ORDER BY 建议了
if len(ignore) == 0 {
// 没有非等值查询条件时可以再为GroupBy和OrderBy添加索引
// 没有非等值查询条件时可以再为 GroupBy 和 OrderBy 添加索引
for _, index := range idxAdv.groupBy {
mergeIndex(indexList, index)
}
// OrderBy
// 没有GroupBy时可以为OrderBy加索引
// 没有 GroupBy 时可以为 OrderBy 加索引
if len(idxAdv.groupBy) == 0 {
for _, index := range idxAdv.orderBy {
mergeIndex(indexList, index)
......@@ -295,14 +295,14 @@ func (idxAdv *IndexAdvisor) IndexAdvise() IndexAdvises {
}
}
} else {
// 未指定Where条件的,只需要GroupBy和OrderBy的索引建议
// 未指定 Where 条件的,只需要 GroupBy 和 OrderBy 的索引建议
for _, index := range idxAdv.groupBy {
mergeIndex(indexList, index)
}
// OrderBy
// 没有GroupBy时可以为OrderBy加索引
// 没有where条件时OrderBy的索引仅能够在索引覆盖的情况下被使用
// 没有GroupBy 时可以为 OrderBy 加索引
// 没有 where 条件时 OrderBy 的索引仅能够在索引覆盖的情况下被使用
// if len(idxAdv.groupBy) == 0 {
// for _, index := range idxAdv.orderBy {
......@@ -329,8 +329,8 @@ func (idxAdv *IndexAdvisor) IndexAdvise() IndexAdvises {
indexes = mergeAdvices(indexes, idxAdv.buildJoinIndex(joinTableMeta)...)
if common.Config.TestDSN.Disable || common.Config.OnlineDSN.Disable {
// 无env环境下只提供单列索引,无法确定table时不给予优化建议
// 仅有table信息时给出的建议不包含DB信息
// 无 env 环境下只提供单列索引,无法确定 table 时不给予优化建议
// 仅有 table 信息时给出的建议不包含 DB 信息
indexes = mergeAdvices(indexes, idxAdv.buildIndexWithNoEnv(indexList)...)
} else {
// 给出尽可能详细的索引建议
......@@ -339,16 +339,16 @@ func (idxAdv *IndexAdvisor) IndexAdvise() IndexAdvises {
indexes = mergeAdvices(indexes, subQueryAdvises...)
// 在开启env的情况下,检查数据库版本,字段类型,索引总长度
// 在开启 env 的情况下,检查数据库版本,字段类型,索引总长度
indexes = idxAdv.idxColsTypeCheck(indexes)
// 在开启env的情况下,会对索引进行检查,对全索引进行过滤
// 在前几步都不会对idx生成DDL语句,DDL语句在这里生成
// 在开启 env 的情况下,会对索引进行检查,对全索引进行过滤
// 在前几步都不会对 idx 生成 DDL 语句,DDL语句在这里生成
return idxAdv.mergeIndexes(indexes)
}
// idxColsTypeCheck 对超长的字段添加前缀索引,剔除无法添索引字段的列
// TODO 暂不支持fulltext索引,
// TODO: 暂不支持 fulltext 索引,
func (idxAdv *IndexAdvisor) idxColsTypeCheck(idxList []IndexInfo) []IndexInfo {
if common.Config.TestDSN.Disable {
return rmSelfDupIndex(idxList)
......@@ -363,7 +363,7 @@ func (idxAdv *IndexAdvisor) idxColsTypeCheck(idxList []IndexInfo) []IndexInfo {
idxBytesTotal := 0
isOverFlow := false
for _, col := range idx.ColumnDetails {
// 获取字段bytes
// 获取字段 bytes
bytes := col.GetDataBytes(common.Config.OnlineDSN.Version)
tmpCol := col.Name
overFlow := 0
......@@ -474,7 +474,7 @@ func (idxAdv *IndexAdvisor) mergeIndexes(idxList []IndexInfo) []IndexInfo {
var indexes []IndexInfo
for _, idx := range idxList {
// 将DB替换成vEnv中的数据库名称
// 将 DB 替换成 vEnv 中的数据库名称
dbInVEnv := idx.Database
if _, ok := idxAdv.vEnv.DBRef[idx.Database]; ok {
dbInVEnv = idxAdv.vEnv.DBRef[idx.Database]
......@@ -503,7 +503,7 @@ func (idxAdv *IndexAdvisor) mergeIndexes(idxList []IndexInfo) []IndexInfo {
var cols []string
var colsDetail []*common.Column
// 把已经存在的key摘出来遍历一遍对比是否是包含关系
// 把已经存在的 key 摘出来遍历一遍对比是否是包含关系
for _, col := range indexMeta.FindIndex(database.IndexKeyName, existedIdx.KeyName) {
cols = append(cols, col.ColumnName)
colsDetail = append(colsDetail, &common.Column{
......@@ -532,7 +532,7 @@ func (idxAdv *IndexAdvisor) mergeIndexes(idxList []IndexInfo) []IndexInfo {
}
// 库、表、列名需要用反撇转义
// TODO 关于外键索引去重的优雅解决方案
// TODO: 关于外键索引去重的优雅解决方案
if !isConstraint {
if common.Config.AllowDropIndex {
alterSQL := fmt.Sprintf("alter table `%s`.`%s` drop index `%s`", idx.Database, idx.Table, idxName)
......@@ -736,12 +736,12 @@ func CompleteColumnsInfo(stmt sqlparser.Statement, cols []*common.Column, env *e
return cols
}
// 从Ast中拿到DBStructure,包含所有表的相关信息
// 从 Ast 中拿到 DBStructure,包含所有表的相关信息
dbs := ast.GetMeta(stmt, nil)
// 此处生成的meta信息中不应该含有""db的信息,若DB为空则认为是已传入的db为默认db并进行信息补全
// 此处生成的 meta 信息中不应该含有""db的信息,若 DB 为空则认为是已传入的 db 为默认 db 并进行信息补全
// BUG Fix:
// 修补dbs中空DB的导致后续补全列信息时无法获取正确table名称的问题
// 修补 dbs 中空 DB 的导致后续补全列信息时无法获取正确 table 名称的问题
if _, ok := dbs[""]; ok {
dbs[env.Database] = dbs[""]
delete(dbs, "")
......@@ -829,7 +829,7 @@ func CompleteColumnsInfo(stmt sqlparser.Statement, cols []*common.Column, env *e
// 将已经获取到正确表信息的列信息带入到env中,利用show columns where table 获取库表信息
// 此出会传入之前从ast中,该 db 下获取的所有表来作为where限定条件,
// 防止与SQL无关的库表信息干扰准确性
// 此处传入的是测试环境,DB是经过变换的,所以在寻找列名的时候需要将DB名称转换成测试环境中经过hash的DB名称
// 此处传入的是测试环境,DB 是经过变换的,所以在寻找列名的时候需要将 DB 名称转换成测试环境中经过 hash 的 DB 名称
// 不然会找不到col的信息
realCols, err := env.FindColumn(col.Name, env.DBHash(db), dbs.Tables(db)...)
if err != nil {
......@@ -840,7 +840,7 @@ func CompleteColumnsInfo(stmt sqlparser.Statement, cols []*common.Column, env *e
// 对比 column 信息中的表名与从 env 中获取的库表名的一致性
for _, realCol := range realCols {
if col.Name == realCol.Name {
// 如果查询到了列名一致,但从ast中获取的列的前缀与env中的表信息不符
// 如果查询到了列名一致,但从 ast 中获取的列的前缀与 env 中的表信息不符
// 1.存在一个同名列,但不同表,该情况下忽略
// 2.存在一个未正确转换的别名(如表名为),该情况下修正,大概率是正确的
if col.Table != "" && col.Table != realCol.Table {
......@@ -897,7 +897,7 @@ func (idxAdv *IndexAdvisor) calcCardinality(cols []*common.Column) []*common.Col
continue
}
// 将获取的索引信息以db.tb维度组织到IndexMeta
// 将获取的索引信息以db.tb 维度组织到 IndexMeta
idxAdv.IndexMeta[realDB][col.Table] = indexInfo
}
......@@ -1039,7 +1039,7 @@ func DuplicateKeyChecker(conn *database.Connector, databases ...string) map[stri
}
}
// 不指定DB的时候检查online dsn中的DB
// 不指定 DB 的时候检查 online dsn 中的 DB
if len(databases) == 0 {
databases = append(databases, tmpOnline.Database)
}
......
......@@ -989,7 +989,7 @@ func init() {
Case: "SELECT DISTINCT c.c_id, c.c_name FROM c,e WHERE e.c_id = c.c_id",
Func: (*Query4Audit).RuleDistinctJoinUsage,
},
// TODO: 5.6有了semi join还要把in转成exists么?
// TODO: 5.6有了semi join 还要把 in 转成e xists 么?
// Use EXISTS instead of IN to check existence of data.
// http://www.winwire.com/25-tips-to-improve-sql-query-performance/
"SUB.004": {
......
......@@ -38,6 +38,7 @@ import (
func main() {
// 全局变量
var err error
var sql string // 单条评审指定的 sql 或 explain
sqlCounter := 1 // SQL 计数器
lineCounter := 1 // 行计数器
......@@ -45,15 +46,8 @@ func main() {
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 文件所在路径
// 配置文件&命令行参数解析
err = common.ParseConfig(common.ArgConfig())
common.LogIfWarn(err, "")
initConfig()
// 打印支持启发式建议
if common.Config.ListHeuristicRules {
......@@ -496,6 +490,33 @@ func main() {
}
}
func initConfig() {
// 更新 binary 文件所在路径为 BaseDir
ex, err := os.Executable()
if err != nil {
panic(err)
}
common.BaseDir = filepath.Dir(ex)
for i, c := range os.Args {
// 如果指定了 -config, 它必须是第一个参数
if strings.HasPrefix(c, "-config") && i != 1 {
fmt.Println("-config must be the first arg")
os.Exit(1)
}
// 等号两边请不要加空格
if c == "=" {
// -config = soar.yaml not support
fmt.Println("wrong format, no space between '=', eg: -config=soar.yaml")
os.Exit(1)
}
}
// 加载配置文件,处理命令行参数
err = common.ParseConfig(common.ArgConfig())
common.LogIfWarn(err, "")
}
func shutdown(vEnv *env.VirtualEnv, rEnv *database.Connector) {
if common.Config.DropTestTemporary {
vEnv.CleanUp()
......
......@@ -78,18 +78,18 @@ type Configration struct {
RewriteRules []string `yaml:"rewrite-rules"` // 生效的重写规则
BlackList string `yaml:"blacklist"` // blacklist 中的 SQL 不会被评审,可以是指纹,也可以是正则
MaxJoinTableCount int `yaml:"max-join-table-count"` // 单条 SQL 中 JOIN 表的最大数量
MaxGroupByColsCount int `yaml:"max-group-by-cols-count"` // 单条SQL中GroupBy包含列的最大数量
MaxDistinctCount int `yaml:"max-distinct-count"` // 单条SQL中Distinct的最大数量
MaxGroupByColsCount int `yaml:"max-group-by-cols-count"` // 单条 SQL 中 GroupBy 包含列的最大数量
MaxDistinctCount int `yaml:"max-distinct-count"` // 单条 SQL 中 Distinct 的最大数量
MaxIdxColsCount int `yaml:"max-index-cols-count"` // 复合索引中包含列的最大数量
MaxTotalRows int64 `yaml:"max-total-rows"` // 计算散粒度时,当数据行数大于 MaxTotalRows即开启数据库保护模式,散粒度返回结果可信度下降
MaxTotalRows int64 `yaml:"max-total-rows"` // 计算散粒度时,当数据行数大于 MaxTotalRows 即开启数据库保护模式,散粒度返回结果可信度下降
MaxQueryCost int64 `yaml:"max-query-cost"` // last_query_cost 超过该值时将给予警告
SpaghettiQueryLength int `yaml:"spaghetti-query-length"` // SQL最大长度警告,超过该长度会给警告
AllowDropIndex bool `yaml:"allow-drop-index"` // 允许输出删除重复索引的建议
MaxInCount int `yaml:"max-in-count"` // IN()最大数量
MaxIdxBytesPerColumn int `yaml:"max-index-bytes-percolumn"` // 索引中单列最大字节数,默认767
MaxIdxBytes int `yaml:"max-index-bytes"` // 索引总长度限制,默认3072
TableAllowCharsets []string `yaml:"table-allow-charsets"` // Table允许使用的DEFAULT CHARSET
TableAllowEngines []string `yaml:"table-allow-engines"` // Table允许使用的Engine
TableAllowCharsets []string `yaml:"table-allow-charsets"` // Table 允许使用的 DEFAULT CHARSET
TableAllowEngines []string `yaml:"table-allow-engines"` // Table 允许使用的 Engine
MaxIdxCount int `yaml:"max-index-count"` // 单张表允许最多索引数
MaxColCount int `yaml:"max-column-count"` // 单张表允许最大列数
IdxPrefix string `yaml:"index-prefix"` // 普通索引建议使用的前缀
......@@ -98,16 +98,16 @@ type Configration struct {
MaxVarcharLength int `yaml:"max-varchar-length"` // varchar最大长度
// ++++++++++++++EXPLAIN检查项+++++++++++++
ExplainSQLReportType string `yaml:"explain-sql-report-type"` // EXPLAIN markdown格式输出SQL样式,支持sample, fingerprint, pretty
ExplainSQLReportType string `yaml:"explain-sql-report-type"` // EXPLAIN markdown 格式输出 SQL 样式,支持 sample, fingerprint, pretty 等
ExplainType string `yaml:"explain-type"` // EXPLAIN方式 [traditional, extended, partitions]
ExplainFormat string `yaml:"explain-format"` // FORMAT=[json, traditional]
ExplainWarnSelectType []string `yaml:"explain-warn-select-type"` // 哪些select_type不建议使用
ExplainWarnAccessType []string `yaml:"explain-warn-access-type"` // 哪些access type不建议使用
ExplainMaxKeyLength int `yaml:"explain-max-keys"` // 最大key_len
ExplainMinPossibleKeys int `yaml:"explain-min-keys"` // 最小possible_keys警告
ExplainWarnSelectType []string `yaml:"explain-warn-select-type"` // 哪些 select_type 不建议使用
ExplainWarnAccessType []string `yaml:"explain-warn-access-type"` // 哪些 access type 不建议使用
ExplainMaxKeyLength int `yaml:"explain-max-keys"` // 最大 key_len
ExplainMinPossibleKeys int `yaml:"explain-min-keys"` // 最小 possible_keys 警告
ExplainMaxRows int `yaml:"explain-max-rows"` // 最大扫描行数警告
ExplainWarnExtra []string `yaml:"explain-warn-extra"` // 哪些extra信息会给警告
ExplainMaxFiltered float64 `yaml:"explain-max-filtered"` // filtered大于该配置给出警告
ExplainWarnExtra []string `yaml:"explain-warn-extra"` // 哪些 extra 信息会给警告
ExplainMaxFiltered float64 `yaml:"explain-max-filtered"` // filtered 大于该配置给出警告
ExplainWarnScalability []string `yaml:"explain-warn-scalability"` // 复杂度警告名单
ShowWarnings bool `yaml:"show-warnings"` // explain extended with show warnings
ShowLastQueryCost bool `yaml:"show-last-query-cost"` // switch with show status like 'last_query_cost'
......@@ -376,7 +376,7 @@ func version() {
fmt.Println("GitDirty:", GitDirty)
}
// 因为vitess sqlparser使用了glog中也会使用flag,为了不让用户困扰我们单独写一个usage
// 因为vitess sqlparser 使用了 glog 中也会使用 flag,为了不让用户困扰我们单独写一个 usage
func usage() {
regPwd := regexp.MustCompile(`:.*@`)
vitessHelp := []string{
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册