1 背景
咱们平时会写各种各样或简略或简单的 sql 语句,提交后就会失去咱们想要的后果集。比方 sql 语句,”select * from t\_user where user\_id > 10;”,意在从表 t\_user 中筛选出 user\_id 大于 10 的所有记录。你有没有想过从一条 sql 到一个后果集,这两头经验了多少崎岖呢?
2 SQL 引擎
从 MySQL、Oracle、TiDB、CK,到 Hive、HBase、Spark,从关系型数据库到大数据计算引擎,他们大都能够借助 SQL 引擎,实现“承受一条 sql 语句而后返回查问后果”的性能。
他们外围的执行逻辑都是一样的,大抵能够通过上面的流程来概括:
两头蓝色局部则代表了 SQL 引擎的根本工作流程,其中的词法剖析和语法分析,则能够引申出“形象语法树”的概念。
3 形象语法树
3.1 概念
高级语言的解析过程都依赖于解析树(Parse Tree),形象语法树(AST,Abstract Syntax Tree)是疏忽了一些解析树蕴含的一些语法信息,剥离掉一些不重要的细节,它是源代码语法结构的一种形象示意。以树状的模式体现编程语言的构造,树的每个节点 ASTNode 都示意源码中的一个构造;AST 在不同语言中都有各自的实现。
解析的实现过程这里不去深刻分析,重点在于当 SQL 提交给 SQL 引擎后,首先会通过词法剖析进行“分词”操作,而后利用语法解析器进行语法分析并造成 AST。
下图对应的 SQL 则是“select username,ismale from userInfo where age>20 and level>5 and 1=1”;
这棵形象语法树其实就简略的能够了解为逻辑执行打算了,它会通过查问优化器利用一些规定进行逻辑打算的优化,失去一棵优化后的逻辑打算树,咱们所熟知的“谓词下推”、“剪枝”等操作其实就是在这个过程中实现的。失去逻辑打算后,会进一步转换成可能真正进行执行的物理打算,例如怎么扫描数据,怎么聚合各个节点的数据等。最初就是依照物理打算来一步一步的执行了。
3.2 ANTLR4
解析(词法和语法)这一步,很多 SQL 引擎采纳的是 ANTLR4 工具实现的。ANTLR4 采纳的是构建 G4 文件,外面通过正则表达式、特定语法结构,来形容指标语法,进而在应用时,依赖语法字典一样的构造,将 SQL 进行拆解、封装,进而提取须要的内容。下图是一个形容 SQL 构造的 G4 文件。
3.3 示例
3.2.1 SQL 解析
在 java 中的实现一次 SQL 解析,获取 AST 并从中提取出表名。
首先引入依赖:
<dependency>
<groupId>org.antlr</groupId>
<artifactId>antlr4-runtime</artifactId>
<version>4.7</version>
</dependency>
在 IDEA 中装置 ANTLR4 插件;
示例 1,解析 SQL 表名。
应用插件将形容 MySQL 语法的 G4 文件,转换为 java 类(G4 文件疏忽)。
类的构造如下:
其中 SqlBase 是 G4 文件名转换而来,SqlBaseLexer 的作用是词法解析,SqlBaseParser 是语法解析,由它生成 AST 对象。HelloVisitor 和 HelloListener:进行形象语法树的遍历,个别都会提供这两种模式,Visitor 访问者模式和 Listener 监听器模式。如果想本人定义遍历的逻辑,能够继承这两个接口,实现对应的办法。
读取表名过程,是重写 SqlBaseBaseVisitor 的几个要害办法,其中 TableIdentifierContext 是表定义的内容;
SqlBaseParser 下还有 SQL 其余“词语”的定义,对应的就是 G4 文件中的各类形容。比方 TableIdentifierContext 对应的是 G4 中 TableIdentifier 的形容。
3.2.2 字符串解析
下面的 SQL 解析过程比较复杂,以一个简略字符串的解析为例,理解一下 ANTLR4 的逻辑。
1)定义一个字符串的语法:Hello.g4
2)应用 IDEA 插件,将 G4 文件解析为 java 类
3)语法解析类 HelloParser,内容就是咱们定义的 h 和 world 两个语法规定,外面具体本义了 G4 文件的内容。
4)HelloBaseVisitor 是采纳访问者模式,凋谢进去的接口,须要自行实现,能够获取 xxxParser 中的规定信息。
5)编写测试类,应用解析器,辨认字符串“hi abc”:
6)调试后发现命中规定 h,解析为 Hi 和 abc 两局部。
7)如果是 SQL 的解析,则会一层层的获取到 SQL 中的各类要害 key。
4 SqlParser
利用 ANTLR4 进行语法解析,是比拟底层的实现,因为 Antlr4 的后果,只是简略的文法解析,如果要进行更加深刻的解决,就须要对 Antlr4 的后果进行更进一步的解决,以更合乎咱们的应用习惯。
利用 ANTLR4 去生成并解析 AST 的过程,相当于咱们在写 rpc 框架前,先去实现一个 netty。因而在工业生产中,会间接采纳已有工具来实现解析。
Java 生态中较为风行的 SQL Parser 有以下几种(此处摘自网络):
- fdb-sql-parser 是 FoundationDB 在被 Apple 收买前开源的 SQL Parser,目前已无人保护。
- jsqlparser 是基于 JavaCC 的开源 SQL Parser,是 General SQL Parser 的 Java 实现版本。
- Apache calcite 是一款开源的动态数据治理框架,它具备 SQL 解析、SQL 校验、查问优化、SQL 生成以及数据连贯查问等性能,罕用于为大数据工具提供 SQL 能力,例如 Hive、Flink 等。calcite 对规范 SQL 反对良好,然而对传统的关系型数据方言反对度较差。
- alibaba druid 是阿里巴巴开源的一款 JDBC 数据库连接池,但其为监控而生的理念让其人造具备了 SQL Parser 的能力。其自带的 Wall Filer、StatFiler 等都是基于 SQL Parser 解析的 AST。并且反对多种数据库方言。
Apache Sharding Sphere(原当当 Sharding-JDBC,在 1.5.x 版本后自行实现)、Mycat 都是国内目前大量应用的开源数据库中间件,这两者都应用了 alibaba druid 的 SQL Parser 模块,并且 Mycat 还开源了他们在选型时的比照剖析 Mycat 路由新解析器选型剖析与后果.
4.1 利用场景
当咱们拿到 AST 后,能够做什么?
- 语法审核:依据内置规定,对 SQL 进行审核、合法性判断。
- 查问优化:依据 where 条件、聚合条件、多表 Join 关系,给出索引优化倡议。
- 改写 SQL:对 AST 的节点进行增减。
- 生成 SQL 特色:参考 JIRA 的慢 SQL 工单中,生成的指纹(不肯定是 AST 形式,但 AST 能够实现)。
4.2 改写 SQL
提到改写 SQL,可能第一个思路就是在 SQL 中增加占位符,再进行替换;再或者利用正则匹配关键字,这种形式局限性比拟大,而且从平安角度不可取。
基于 AST 改写 SQL,是用 SQL 字符串生成 AST,再对 AST 的节点进行调整;通过遍历 Tree,拿到指标节点,减少或批改节点的子节点,再将 AST 转换为 SQL 字符串,实现改写。这是在满足 SQL 语法的前提下实现的平安改写。
以 Druid 的 SQL Parser 模块为例,利用其中的 SQLUtils 类,实现 SQL 改写。
4.2.1 新增改写
1)原始 SQL
2)理论执行 SQL
4.2.2 查问改写
后面省略了 Tree 的遍历过程,须要辨认诸如 join、sub-query 等语法
1)简略 join 查问
- 原始 SQL
- 理论执行 SQL
2)join 查问 + 隐式 where 条件
- 原始 SQL
- 理论执行 SQL
3)union 查问 +join 查问 + 子查问 + 显示 where 条件
- 原始 SQL
(unionQuality\_Union\_Join\_SubQuery\_ExplicitCondition)
- 理论执行 SQL
5 总结
本文是基于环境隔离的技术预研过程产生的,其中改写 SQL 的实现,是数据库在数据隔离上的一种尝试。
能够让开发人员无感知的状况下,以插件模式,在 SQL 提交到 MySQL 前实现动静改写,只须要在数据表上减少字段、标识环境差别,后续 CRUD 的 SQL 都会主动减少标识字段(flag=’预发’、flag=’生产’),所操作的数据只能是以后利用所在环境的数据。
作者:耿宏宇