前言
经常可以看到市面上的SQL审核工具号称自己审核规则很多,支持的数据库类型很全。实际使用了以后,就会发现审核规则确实很多,一个MySQL数据库类型近200个规则,实际可用、有用的可能就几十条;而对于其他类型数据库,譬如PostgreSQL或openGauss,规则一共可能就几十条。稍微复杂一点的SQL就报语法错误;或是出现大面积的误报或漏报的情况。审查规则越多越好吗?市场上到底需要什么样的SQL审核引擎?看一下SQL优化领域的领先厂商PawSQL如何设计它的SQL审核引擎。
PawSQL审核引擎的设计目标
简单来说,PawSQL审核引擎的设计目标就是:使用尽量少的审核规则,覆盖尽量全的SQL语法和结构,确保尽量高的审核正确率。
目标1. 尽量少的审核规则
我们通过抽象化、参数化、层级化三个策略来实现尽量少审查规则的目标。
1.1 抽象化
想要审核规则尽量少,但是覆盖度却尽量广,就需要对审核规则的模式进行抽象化,这样才能用同一个规则覆盖不同的数据库类型的SQL,不同的SQL写法。
譬如对于`表连接缺少链接条件`这个审查规则,它既包括通过`JOIN`操作符显式的表连接(案例1),也包括FROM多张表,但是在`WHERE`里缺少连接条件的情况(案例2);同时它也适用于MySQL/PostgreSQL/openGauss等各种数据库。
SELECT *
FROM CUSTOMER INNER JOIN ORDERS
ON 1 =1
WHERE O.O_CUSTKEY IN (1,2)
SELECT *
FROM CUSTOMER, ORDERS
WHERE O.O_CUSTKEY IN (1,2)
1.2 参数化
另一个减少规则数的方式是通过参数化,参数化既可以通过表达式,或是通过阈值来进行。
譬如使用`禁止使用列表中的数据类型`这个规则来替代`禁止使用ENUM类型`、`禁止使用SET类型`、`禁止使用TIMESTAMP类型`三个规则。而且用户可以根据不同的数据库类型自定义自己的禁止使用的数据类型。
1.3 层级化
很多时候,用户根据特定的场景,对于SQL的审核可能会有不同的松紧度。PawSQL的审核规则体系对规则进行层级化的设计,当更严格的规则被采用时,它下级的审核规则就被覆盖,而不需要参与审核了。
譬如当禁止`删除列的规则`被启用时,其项下的两个规则`删除的列名需满足指定规范`和`禁止删除索引中的列`就不会参与审核了;从而提升了审核的效率,同时避免了无效告警的干扰。
目标2.覆盖尽量多的SQL语句结构
SQL语句的结构可以从以下几个维度进行分类,根据这些维度的不同组合,SQL语句呈现出多样的结构形态,复杂程度也有所不同。PawSQL的审核规则体系基本上做到了除DCL和TCL之外所有类型的全覆盖:
目标3. 确保尽量高的审核正确率
我们从漏报率和误报率两个维度衡量审核的正确率。
降低漏报率
漏报率衡量一个审核规则漏掉一个它本应该发现的SQL质量问题的几率;
审查规则案例1:`禁止使用=NULL来进行空值判断`,
SELECT *
FROM customer
WHERE c_name = NULL
大部分审核引擎都只进行条件中的`=NULL`审核,可以对上面的SQL进行警告。
SELECT
CASE c_name
WHEN NULL
THEN '空名字'
ELSE '非空名字'
END
FROM customer
却忽略了第二个SQL也存在同样的问题,从而导致了程序正确性的严重隐患。
降低误报率
误报率衡量审核的准确率,即报告的问题是否是真实的SQL质量问题。
审查规则案例2:尽量避免使用SELECT *
SQL片段 | 违反 |
---|---|
SELECT * FROM ... | 是 |
SELECT ... WHERE EXISTS(SELECT * FROM ORDERS...) | 否 |
SELECT COUNT(*) FROM ... | 否 |
市面上很多的审核引擎对于复杂规则的误报率都很高,给用户带来了很多困扰。SELECT * 出现在EXISTS子句里是合理的,SELECT COUNT(*)也不应该触发该规则;但是很多审核引擎都对第二或是第三个进行了告警。常见还有如对于`避免使用OR条件`规则,很多审核引擎没有评估`AND`条件已经具有足够的过滤能力,即使条件中已经使用了主键进行过滤了,引擎仍然对此进行预警。