XiaoMi/soar调研/

XiaoMi/soar调研/

github :https://github.com/xiaomi/soar

安装说明 :https://github.com/XiaoMi/soar/blob/master/doc/install.md

源码下载 :https://GOlang.google.cn/dl/

环境准备

环境依赖:

一般依赖

  • go1.17.1(官网上说是1.0+,但我编译的时候报错,所以我直接将 Go 升级为最高版本)
  • git

GO 下载&&安装&&环境变量

# Go 安装
tar -zxvf go1.17.1.linux-amd64.tar.gz

# 环境变量
vi /etc/profile
export PATH=$PATH:/usr/local/go/bin
# GOPATH环境变量:表示go的工作目录 USER_NAME 为用户名
export GOPATH="/home/USER_NAME/go"
export GOPATH="/home/root/go"

# Go 版本查看:
go version

# source更新环境变量:
source /etc/profile

soar 下载&&安装&&编译

# soar 工具下载
wget https://github.com/XiaoMi/soar/releases/download/0.9.0/soar.linux-amd64 -O soar
chmod a+x soar

# 生成二进制文件
go get -d github.com/XiaoMi/soar
cd ${GOPATH}/src/github.com/XiaoMi/soar && make
......
build Success!

安装验证

方法1:
echo 'select * from mz' | ./soar

方法2:
./soar  -query "select * from mz"

方法3:
cat test.sql
select * from mz
cat test.sql | ./soar

结果展示:
# Query: 88E82CE5233AD07E

★ ★ ★ ☆ ☆ 75分

```sql

SELECT  
  * 
FROM  
  mz

##最外层SELECT未指定WHERE条件

* **Item:**  CLA.001

* **Severity:**  L4

* **Content:**  SELECT语句没有WHERE子句,可能检查比预期更多的行(全表扫描)。对于SELECT COUNT(\*)类型的请求如果不要求精度,建议使用SHOW TABLE STATUS或EXPLAIN替代。

##  不建议使用SELECT * 类型查询

* **Item:**  COL.001

* **Severity:**  L1

* **Content:**  当表结构变更时,使用\*通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。

优化建议组成

// Rule 评审规则元数据结构
type Rule struct {
    Item     string                  `json:"Item"`     // 规则代号
    Severity string                  `json:"Severity"` // 危险等级:L[0-8], 数字越大表示级别越高
    Summary  string                  `json:"Summary"`  // 规则摘要
    Content  string                  `json:"Content"`  // 规则解释
    Case     string                  `json:"Case"`     // SQL示例
    Position int                     `json:"Position"` // 建议所处SQL字符位置,默认0表示全局建议
    Func     func(*Query4Audit) Rule `json:"-"`        // 函数名
}

SQL评分(个人感觉没什么用)

当SQL的危险等级(Severity)不同的时候,会对当前的SQL有一个打分的机制。

  • 不同类型的建议指定的Severity不同,严重程度数字由低到高依次排序。满分100分,扣到0分为止。L0不扣分只给出建议,L1扣5分,L2扣10分,每级多扣5分以此类推。当由时给出L1, L2两要建议时扣分叠加,即扣15分。
当然我们可以直接修改rules.go文件来更改扣分建议
vim /home/root/go/src/github.com/XiaoMi/soar/advisor/rules.go

常用命令

打印规则列表

$ soar -list-heuristic-rules

忽略某些规则

$ soar -ignore-rules "*.*"

#以安装验证的SQL为例
echo 'select * from mz' | ./soar -ignore-rules "CLA.001"
# Query: 88E82CE5233AD07E

★ ★ ★ ★ ☆ 95分

```sql

SELECT  
  * 
FROM  
  mz ```

##  不建议使用SELECT * 类型查询

* **Item:**  COL.001

* **Severity:**  L1

* **Content:**  当表结构变更时,使用\*通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。

打印支持的报告格式

$ soar -list-report-types

语法检查工具

$ echo "select * from mz" | soar -only-syntax-check

$ echo "select * frommz" | soar -only-syntax-check
At SQL 0 : syntax error at position 16 near 'frommz'

SQL指纹

SQL 指纹指将一条 SQL 中的字面值替换成其他固定符号。可以用来做 SQL 脱敏或者 SQL 归类。

$ echo "select * from mz where col='abc'" | soar -report-type=fingerprint

输出

select * from mz where col=?

将UPDATE/DELETE/INSERT语法转为SELECT

$ echo "update mz set dba = 'abc'" | soar -rewrite-rules dml2select,delimiter  -report-type rewrite

输出

select * from mz;

合并多条ALTER语句

$ echo "alter table mz add column a int; alter table mz add column b int;" | ./soar -report-type rewrite -rewrite-rules mergealter

输出

ALTER TABLE `mz` add column a int, add column b int ;

SQL美化

$ echo "select * from mz where col = 'val'" | ./soar -report-type=pretty

输出

SELECT  
  * 
FROM  
  mz  
WHERE  
  col  = 'val';

EXPLAIN信息分析报告

在这里插入图片描述在这里插入图片描述

工具测试

案例

  1. 尽量不用Select *

    echo 'SELECT * FROM mz WHERE a = 123;' | ./soar 
    # Query: 00D9A34797B3F9DF
    ★ ★ ★ ★ ☆ 95##  不建议使用SELECT * 类型查询
    * **Item:**  COL.001
    * **Severity:**  L1
    * **Content:**  当表结构变更时,使用\*通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
    
  2. 改写Or为IN()

    echo 'SELECT a FROM mz WHERE a = 123 or a = 222 ;' | ./soar 
    # Query: B1392DCB03D1DE3B
    ★ ★ ★ ★ ☆ 100= 222
    
    ##  OR查询索引列时请尽量使用IN谓词
    * **Item:**  ARG.008
    * **Severity:**  L1
    * **Content:**  IN-list谓词可以用于索引检索,并且优化器可以对IN-list进行排序,以匹配索引的排序序列,从而获得更有效的检索。请注意,IN-list必须只包含常量,或在查询块执行期间保持常量的值,例如外引用。
    
  3. 改写Or为Union

    echo 'SELECT a FROM mz WHERE a = 123 or b = "测试" ;' | ./soar 
    # Query: BE88A6C83D6E8BE9
    ★ ★ ★ ★ ☆ 95##  OR查询索引列时请尽量使用IN谓词
    * **Item:**  ARG.008
    * **Severity:**  L1
    * **Content:**  IN-list谓词可以用于索引检索,并且优化器可以对IN-list进行排序,以匹配索引的排序序列,从而获得更有效的检索。请注意,IN-list必须只包含常量,或在查询块执行期间保持常量的值,例如外引用。
    
  4. 避免负向查询和%前缀模糊查询

    %前缀模糊查询

    echo 'SELECT a FROM tbl WHERE a like "%est";' | ./soar
    # Query: C15F7A5947239AFF
    ★ ★ ★ ★ ☆ 80##  不建议使用前项通配符查找
    * **Item:**  ARG.001
    * **Severity:**  L4
    * **Content:**  例如“%foo”,查询参数有一个前项通配符的情况无法使用已有索引。
    

    负向查询

    echo 'SELECT a FROM mz WHERE a != 0;' | ./soar 
    # Query: A43CF84521838793
    ★ ★ ★ ★ ☆ 95##  '!=' 运算符是非标准的
    * **Item:**  STA.001
    * **Severity:**  L0
    * **Content:**  "<>"才是标准SQL中的不等于运算符。
    
    /****那就测试下<>/****
    
    echo 'SELECT * FROM mz WHERE a <> 0;' | ./soar 
    # Query: 96BA20EC8863A38D
    ★ ★ ★ ★ ☆ 95##  不建议使用SELECT * 类型查询
    * **Item:**  COL.001
    * **Severity:**  L1
    * **Content:**  当表结构变更时,使用\*通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
    
  5. Count不要使用在可Null的字段上面

    echo 'SELECT count(a) FROM mz ' | ./soar 
    # Query: 6FF2DE9F51E93F69
    
    ★ ★ ★ ☆ ☆ 75##  最外层SELECT未指定WHERE条件
    * **Item:**  CLA.001
    * **Severity:**  L4
    * **Content:**  SELECT语句没有WHERE子句,可能检查比预期更多的行(全表扫描)。对于SELECT COUNT(\*)类型的请求如果不要求精度,建议使用SHOW TABLE STATUSEXPLAIN替代。
    
    ##  不建议使用COUNT(col)或COUNT(常量)
    * **Item:**  FUN.005
    * **Severity:**  L1
    * **Content:**  不要使用COUNT(col)COUNT(常量)来替代COUNT(\*),COUNT(\*)是SQL92定义的标准统计行数的方法,跟数据无关,跟NULL和非NULL也无关。
    
    echo 'SELECT count(a) FROM mz where id = 1' | ./soar
    # Query: C1C66F0877D1A5FB
    
    ★ ★ ★ ★ ☆ 90##  指定了WHERE条件或非MyISAM引擎时使用COUNT(*)操作性能不佳
    * **Item:**  FUN.002
    * **Severity:**  L1
    * **Content:**  COUNT(\*)的作用是统计表行数,COUNT(COL)的作用是统计指定列非NULL的行数。MyISAM表对于COUNT(\*)统计全表行数进行了特殊的优化,通常情况下非常快。但对于非MyISAM表或指定了某些WHERE条件,COUNT(\*)操作需要扫描大量的行才能获取精确的结果,性能也因此不佳。有时候某些业务场景并不需要完全精确的COUNT值,此时可以用近似值来代替。EXPLAIN出来的优化器估算的行数就是一个不错的近似值,执行EXPLAIN并不需要真正去执行查询,所以成本很低。
    
    ##  不建议使用COUNT(col)或COUNT(常量)
    * **Item:**  FUN.005
    * **Severity:**  L1
    * **Content:**  不要使用COUNT(col)COUNT(常量)来替代COUNT(\*),COUNT(\*)是SQL92定义的标准统计行数的方法,跟数据无关,跟NULL和非NULL也无关。
    
  6. 减少Count(*)

    echo 'SELECT count(*) FROM mz where id = 1' | ./soar 
    # Query: ED0ADC5B79B65731
    
    ★ ★ ★ ★ ☆ 100
  7. 使用Union ALL 而不用Union

    echo 'SELECT a FROM mz1 where id = 1 UNION   SELECT * FROM mz2 where id = 1 UNION   SELECT * FROM mz3 where id = 1 ;' | ./soar 
    # Query: A3C01672F2C16A6E
    ★ ★ ★ ☆ ☆ 65##  如果您不在乎重复的话,建议使用UNION ALL替代UNION
    * **Item:**  SUB.002
    * **Severity:**  L2
    * **Content:**  与去除重复的UNION不同,UNION ALL允许重复元组。如果您不关心重复元组,那么使用UNION ALL将是一个更快的选项。
    
  8. 分解链接,保证高并发

    echo 'Select * from tag JOIN tag_post on tag_post.tag_id=tag.id JOIN post on tag_post.post_id=post.id WHERE tag.tag="wasd";' | ./soar 
    # Query: 389790AA0277DF13
    ★ ★ ★ ★ ☆ 100分
     
    OK
    
    echo 'SELECT a  FROM mz where a = (select a from mz2 where id = 1 )' | ./soar
    # Query: 54F07BDE792CAB12
    
    ★ ★ ★ ★ ☆ 80##  MySQL对子查询的优化效果不佳
    * **Item:**  SUB.001
    * **Severity:**  L4
    * **Content:**  MySQL将外部查询中的每一行作为依赖子查询执行子查询。 这是导致严重性能问题的常见原因。这可能会在 MySQL 5.6版本中得到改善, 但对于5.1及更早版本, 建议将该类查询分别重写为JOINLEFT OUTER JOIN
  9. Group By 去除排序

    echo 'SELECT a FROM tbl where a = 1 group by a' | ./soar 
    # Query: 21B94D13243AC243
    ★ ★ ★ ☆ ☆ 70##  请为GROUP BY显示添加ORDER BY条件
    * **Item:**  CLA.008
    * **Severity:**  L2
    * **Content:**  默认MySQL会对'GROUP BY col1, col2, ...'请求按如下顺序排序'ORDER BY col1, col2, ...'。如果GROUP BY语句不指定ORDER BY条件会导致无谓的排序产生,如果不需要排序建议添加'ORDER BY NULL'
  10. 同数据类型的列值比较

    echo 'SELECT a  FROM tbl where a = 11 and b = test' | ./soar 
    # Query: D288099B2EBD5368
    ★ ★ ★ ★ ☆ 100分
    
    OK
    

结论

在这里插入图片描述

对于这款开源产品,对于一个 SQL优化的新手还是非常不错的,给出建议等也非常通俗易懂,但是建议好像是根据关键字形成的固定建议,所以对于复杂的 SQL来说不太友好,还有一个就是对索引建议不明确,还是要靠自己的优化经验来优化。只能给一些我们日常的SQL优化规则上的建议。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值