作者:孟祥松

全文共4708个字,建议阅读12分钟



01

前言

提起SQL相信大家都不陌生,SQL是结构化查询语言(Structure Query Language),是我们与数据库打交道最常用的方式。一般的使用场景下,我们更多的关注如何使用SQL查询数据,以及如何快速的获取到数据,但是很少注意到SQL本身的潜在价值。

SQL中包含了所有我们要查询的数据信息,里面存储着所有我们与数据库交互的信息。从SQL中可以挖掘出很多有用的信息,比如,了解用户使用表的次数,进行单表还是多表查询,SQL中是否包含慢查询,同时也可以进行对用户SQL的权限管理与规则管控等。

那么如何从SQL中挖掘信息呢?这就要提到解析引擎SQLParser。解析引擎对于数据库是极其重要的,每个数据库都会有一套解析引擎,以便对客户端传来的SQL进行解析后得到有用的信息,构造执行计划,然后执行下推计算,得到用户想要的信息返回给用户,下图展示的是数据库的执行逻辑。

自研SQL解析引擎,让你的数仓更省心~_sql


02

SQL解析简介

我们交流的语言是自然语言,比如汉语、英语和法语。这类语言不是人为设计(虽然有人试图强加一些规则)而是自然进化的。形式语言是为了特定应用而人为设计的语言。例如数学家用的数字和运算符号、化学家用的分子式等。编程语言也是一种形式语言,是专门设计用来表达计算过程的形式语言。形式语言有严格的语法(Syntax)规则,语法规则是由符号(Token)和结构(Structure)的规则所组成的。

当阅读一个自然语言的句子或者一种形式语言的语句时,你不仅要搞清楚每个词或者Token是什么意思,而且必须搞清楚整个句子的结构是什么样的。这个分析句子结构的过程称为解析。

举个例子:当看到一句英文She is a girl,那么你首先要做词法解析获得She,is,a,girl这个四个单词,然后做主谓宾的语法解析,然后你知道了它表达的意思。SQL解析亦如此。

目前市面上比较流行的解析引擎有Altlr4,JavaCC,Druid SQLParser等,它们拥有共同的结构。

SQLParser的结构与逻辑如下图所示,它主要分为三个部分:

1、Parser:SQL解析器

2、AST:抽象语法树

3、Visitor:访问者

自研SQL解析引擎,让你的数仓更省心~_语法树_02


03

SQLParser原理


Parser是一个SQL解析器,主要的作用是生成AST,Parser主要有两部分组成:词法分析、语法分析。

1、Parser解析器

1.1 lexer词法解析

词法分析,也叫扫描Scanner,它读取我们的代码,从左到右扫描文本,把文本拆成一些单词,然后把它们按照预定的规则(关键字识别器、标识符识别器、常量识别器、操作符识别器)合并成一个个的单词标识 Token,Token在机内一般用形似的二元组来表示,type表示一个单词种类,value为属性值。我们可以把SQL中任意一个关键词当做一个Token,表名列名函数名等可以认为是一个identifier token。同时,它会移除空白符、注释等。最后,整个代码将被分割进一个 Token列表(或者说一维数组)。当它遇到操作符,或者特殊符号EOF的时候,它会认为一个话已经完成了。,>

解析SQL也是差不多的,你需要通过lexer知道是有哪些词,然后这些词构成了什么语法。而经过词法、语法解析以后输出的结果就是抽象语法树。

1.2 Parser语法解析

语法解析,也称解析器,分词阶段完成以后,但是仍不知道Token序列数组中各个单词组合在一起后,表达了什么含义。语法分析的职责就是明确一个语句的语义,表达的什么意思。

SQL语言是一门形式语言。语法解析器主要功能是根据语法规则,解析出一个语句的一个唯一含义。Token序列数组会经过我们的解析器,由解析器识别出代码中的各类短语,会根据预定的SQL语法规则输出解析树,这棵树是对代码的树形描述。同时,验证语法结构。语法如果有错的话,抛出语法错误。

SQL解析,本质上就是把SQL字符串给解析成AST,也就是说SQLParser的输入是SQL字符串,输出是一个AST。你怎么使用这个AST结果又是另外一回事,你可以修改AST,也可以添加点东西等等,但整个过程都是围绕着ast这个东西。接下来会详细讲解一下Parser的第二部分:AST。

2、AST抽象语法树

AST全称是abstract syntax tree,中文直译抽象语法树,AST是Parser的解析结果,所以我们很有必要知道AST是什么样的,也从而知道了Parser做了一堆解析式为了得到什么。    

简单来说,例如一条“select id,name,age from user where id=1;”语句,它由 select关键字、selectlist 列表、from关键字、tableName、where关键字、columnName、操作符、常量值等组成,其中selectlist列表由一个或多个 select 项组成(此处为id),where 子句由一个或者多个 where条件组成。这种总分的逻辑结构,在计算机里通常用树来描述。任何一条SQL都可以用一棵AST来表达。

自研SQL解析引擎,让你的数仓更省心~_数据_03


3、Visitor访问者模式

语法树构造成功了,需要有方法来访问遍历树中的各个节点。SQLParser采用了访问者模式,它的作用就是对外提供访问AST的接口,通过实现不同的visitor可以根据不同的需求来提供不同的访问接口。

AST访问者类,它针对不同类型的节点提供了一系列重载的visit()和endvisit()方法,意思就是访问到该类型的节点时执行visit()方法,访问完该类型节点后执行endvisit()方法。其中,visit()方法需返回boolean类型的返回值,表示是否继续访问子节点。另外ASTVisitor还提供了preVisit()和postVisit()方法,参数类型为ASTNode,也就是说不论哪种类型的节点,访问前后都要分别执行preVisit()和postVisit()。这些方法的具体实现由ASTVisitor的子类负责,如果不需要对所访问到的节点做处理,则无需在ASTVisitor的子类中覆盖这些方法。

SQLParser的访问接口有如下几种:

1、OutputVisitor:用来把AST输出为字符串;

2、WallVisitor:分析SQL语义,防止SQL注入;

3、ParameterizedOutputVisitor:合并未参数化的SQL进行统计;

4、EvalVisitor:对SQL表达式进行求值;

5、ExportParameterVisitor:提取SQL中的变量参数;

6、SchemaStatVisitor:用来统计SQL中使用的表、字段、过滤条件、排序表达式、分组表达式;

7、SQL格式化:druid内置了基于语义的格式化功能。

当然,除了内置的操作,你也可以自定义visitor。


04

SQL解析的功能与应用场景

1、基础功能:语法校验

SQLParser会将每一段SQL解析并构建成语法树结构,然后进行遍历,按照AST的结构会检查出语法结构是否正确,因此具有语法检验的功能。

2、提取多条SQL

一个SQL脚本中往往会有多条SQL语句,并且会携带注释或者其他无关的信息,在词法与语法解析时,SQLparser会将注释等信息与正常SQL区分开,换句话说,具有SQL清洗注释等信息的功能。在解析的过程中,构建语法树结构是基于一段SQL,如果一个脚本中包含很多代码段,parser同样会将SQL脚本分成很多statement,每条statement就是一个SQL代码段。

3、SQL提取表列信息,统计冷热表

每个DDL语句中都会包含表和列的信息。表列信息是重要的组成部分。schemaName,tableName,columnName在SQL中是有一定规律的,比如column通常出现在select list当中,schemaName和tableName通常会在一起出现,tableName 会出现在from后、join后等。SQLParser在遍历语法树的同时可以将schemaName,tableName,columnName提取出来并进行保存。

对保存下来的表列信息进行统计分析,不仅可以对解析的表进行天梯规则管控,而且根据用户查询次数得到分析出冷热表,可以针对性的对热表进行一定的优化。

自研SQL解析引擎,让你的数仓更省心~_语法树_04

4、SQL血缘解析

SQL血缘解析源于数据治理对数据血缘分析的需求。

对于我们大数据开发人员来说,数据流向尤其重要,血缘解析可以反映出数据库表列之间流向关系,形成一个数据地图。通过这个数据地图不仅可以直观的分析出每个表列上下游的来龙去脉,而且方便开发人员的维护与迅速排查问题所在。

通过SQLParser可以做到血缘的精确解析。

通过对离线数据开发平台上所有任务脚本的解析,首先进行SQL清洗与筛选,将SQL注释等无用信息清除掉,并且筛选出create与insert语句,对筛选出的语句进行SQL解析,构建语法树,遍历语法树,对SQL的源表源字段目标表目标字段进行关系梳理与连接。得到我们想要的SQL血缘关系。

自研SQL解析引擎,让你的数仓更省心~_数据_05


5、SQL语句改写

SQL语句改写目前是我行大数据平台要实现冷热数据的分离,且对用户无感知,用户通过原有SQL逻辑可以实现对湖仓数据的查询。

通过对用户查询的表的生命周期日期字段进行判断分析,通过元数据信息表判断该数据表存储位置是在数据湖,还是数据仓库中,然后进行SQL的拼装与改写,不但可以适用于多引擎查询,而且实现用户业务逻辑SQL的完整可靠的查询。

6、SQL DDL语句解析

首先DDL语句解析并获取相关信息意义重大。

源端进行数据库变更操作时,在上线前会通过调用Jenkins统一发版入口经过SQLParser进行审查解析DDL语句,监测元数据信息变更信息,方便及时通知到下游进行相应的数据表整改操作等,间接避免生产事故的发生。

DDL解析主要针对数据库变更表信息,增删列,修改表名等操作

从技术来说,DDL语句解析,同select等DML语句查询,同样会构建语法树,类型为Alter,然后进行遍历,最终将需要的信息保存下来。


05总结

SQL解析对于数据管理与使用人员来说具有很大的意义。凡是有SQL的地方,就会有SQLParser,SQLParser会解析出SQL脚本中的任何信息,一方面可以挖掘用户想要得到的信息,另一方面也可以进行相应的管控,使操作者的所有行为无所遁形。

SQLParser目前主要用于事前把控,事后分析场景,可以避免一定情况下的生产事故,方便数据管理人员自动审核与管控SQL上线,同时便于业务人员挖掘与统计SQL中潜在的信息。





参考文献



​https://github.com/alibaba/druid/wiki/SQL-Parser​



​https://github.com/alibaba/druid/wiki/Druid_SQL_AST​





  

关注不迷路~ 各种福利、资源定期分享