您好!
欢迎来到京东云开发者社区
登录
首页
博文
课程
大赛
工具
用户中心
开源
首页
博文
课程
大赛
工具
开源
更多
用户中心
开发者社区
>
博文
>
数据分片内核剖析——解析引擎
分享
打开微信扫码分享
点击前往QQ分享
点击前往微博分享
点击复制链接
数据分片内核剖析——解析引擎
Apache ShardingSphere
2021-01-15
IP归属:未知
33040浏览
相对于其他编程语言,SQL 是比较简单的。 不过,它依然是一门完善的编程语言,因此对 SQL 的语法进行解析,与解析其他编程语言(如:Java 语言、C 语言、Go 语言等)并无本质区别。 # 抽象语法树 解析过程分为词法解析和语法解析。 词法解析器用于将 SQL 拆解为不可再分的原子符号,称为 Token。并根据不同数据库方言所提供的字典,将其归类为关键字,表达式,字面量和操作符。 再使用语法解析器将 词法解析器的输出 转换为抽象语法树。 例如,以下 SQL: ```sql SELECT id, name FROM t_user WHERE status = 'ACTIVE' AND age > 18 ``` 解析之后的为抽象语法树见下图。 ![](//img1.jcloudcs.com/developer.jdcloud.com/bf6b2b37-ad9a-4d4e-8ffb-57d72e8e66da20210115105330.jpg) 为了便于理解,抽象语法树中的关键字的 Token 用绿色表示,变量的 Token 用红色表示,灰色表示需要进一步拆分。 最后,通过`visitor`对抽象语法树遍历构造域模型,通过域模型(`SQLStatement`)去提炼分片所需的上下文,并标记有可能需要改写的位置。 供分片使用的解析上下文包含查询选择项(Select Items)、表信息(Table)、分片条件(Sharding Condition)、自增主键信息(Auto increment Primary Key)、排序信息(Order By)、分组信息(Group By)以及分页信息(Limit、Rownum、Top)。 SQL 的一次解析过程是不可逆的,一个个 Token 按 SQL 原本的顺序依次进行解析,性能很高。 考虑到各种数据库 SQL 方言的异同,在解析模块提供了各类数据库的 SQL 方言字典。 # SQL 解析引擎 ## 历史 SQL 解析作为分库分表类产品的核心,其性能和兼容性是最重要的衡量指标。 ShardingSphere 的 SQL 解析器经历了 3 代产品的更新迭代。 第一代 SQL 解析器为了追求性能与快速实现,在 1.4.x 之前的版本使用 Druid 作为 SQL 解析器。经实际测试,它的性能远超其它解析器。 第二代 SQL 解析器从 1.5.x 版本开始,ShardingSphere 采用完全自研的 SQL 解析引擎。 由于目的不同,ShardingSphere 并不需要将 SQL 转为一颗完全的抽象语法树,也无需通过访问器模式进行二次遍历。它采用对 SQL `半理解`的方式,仅提炼数据分片需要关注的上下文,因此 SQL 解析的性能和兼容性得到了进一步的提高。 第三代 SQL 解析器从 3.0.x 版本开始,尝试使用 ANTLR 作为 SQL 解析引擎 的生成器,并采用 Visit 的方式从 AST 中获取 SQL Statement。从5.0.x 版本开始,解析引擎的架构已完成重构调整,同时通过将第一次解析的得到的 AST 放入缓存,方便下次直接获取相同 SQL的解析结果,来提高解析效率。 因此我们建议用户采用 `PreparedStatement` 这种 SQL 预编译的方式来提升性能。 ## 功能点 - 提供独立的SQL解析功能 - 可以非常方便的对语法规则进行扩充和修改(使用了`ANTLR`) - 支持多种方言的SQL解析 | 数据库 | 支持状态 | | --- | --- | | MySQL | 支持,完善 | | PostgreSQL | 支持,完善 | | SQLServer | 支持 | | Oracle | 支持 | | SQL92 | 支持 | - 提供SQL格式化功能(开发中) - 提供SQL模板话功能(开发中) ## API使用 引入Maven依赖 ``` <dependency> <groupId>org.apache.shardingsphere</groupId> <artifactId>shardingsphere-sql-parser-engine</artifactId> <version>${project.version}</version> </dependency> // 根据需要引入指定方言的解析模块(以MySQL为例),可以添加所有支持的方言,也可以只添加使用到的 <dependency> <groupId>org.apache.shardingsphere</groupId> <artifactId>shardingsphere-sql-parser-mysql</artifactId> <version>${project.version}</version> </dependency> ``` 例子 - 获取语法树 ``` /** * databaseType type:String 可能值 MySQL,Oracle,PostgreSQL,SQL92,SQLServer * sql type:String 解析的SQL * useCache type:boolean 是否使用缓存 * @return parse tree */ ParseTree tree = new SQLParserEngine(databaseType).parse(sql, useCache); ``` - 获取SQLStatement ``` /** * databaseType type:String 可能值 MySQL,Oracle,PostgreSQL,SQL92,SQLServer * useCache type:boolean 是否使用缓存 * @return SQLStatement */ ParseTree tree = new SQLParserEngine(databaseType).parse(sql, useCache); SQLVisitorEngine sqlVisitorEngine = new SQLVisitorEngine(databaseType, "STATEMENT"); SQLStatement sqlStatement = sqlVisitorEngine.visit(tree); ``` - SQL格式化 ``` /** * databaseType type:String 可能值 MySQL * useCache type:boolean 是否使用缓存 * @return String */ ParseTree tree = new SQLParserEngine(databaseType).parse(sql, useCache); SQLVisitorEngine sqlVisitorEngine = new SQLVisitorEngine(databaseType, "FORMAT"); String formatedSql = sqlVisitorEngine.visit(tree); ``` <br />例子<br /> - 获取语法树 ``` /** * databaseType type:String 可能值 MySQL,Oracle,PostgreSQL,SQL92,SQLServer * sql type:String 解析的SQL * useCache type:boolean 是否使用缓存 * @return parse tree */ ParseTree tree = new SQLParserEngine(databaseType).parse(sql, useCache); ``` - 获取SQLStatement ``` /** * databaseType type:String 可能值 MySQL,Oracle,PostgreSQL,SQL92,SQLServer * useCache type:boolean 是否使用缓存 * @return SQLStatement */ ParseTree tree = new SQLParserEngine(databaseType).parse(sql, useCache); SQLVisitorEngine sqlVisitorEngine = new SQLVisitorEngine(databaseType, "STATEMENT"); SQLStatement sqlStatement = sqlVisitorEngine.visit(tree); ``` - SQL格式化 ``` /** * databaseType type:String 可能值 MySQL * useCache type:boolean 是否使用缓存 * @return String */ ParseTree tree = new SQLParserEngine(databaseType).parse(sql, useCache); SQLVisitorEngine sqlVisitorEngine = new SQLVisitorEngine(databaseType, "FORMAT"); String formatedSql = sqlVisitorEngine.visit(tree); ``` <br />例子: | sql | formatedSql | | --- | --- | | select a+1 as b, name n from table1 join table2 where id=1 and name='lu'; | SELECT a + 1 AS b, name n<br />FROM table1 JOIN table2<br />WHERE<br /> id = 1<br /> and name = 'lu'; | | select id, name, age, sex, ss, yy from table1 where id=1; | SELECT id , name , age , <br /> sex , ss , yy <br />FROM table1<br />WHERE <br /> id = 1; | | select id, name, age, count(*) as n, (select id, name, age, sex from table2 where id=2) as sid, yyyy from table1 where id=1; | SELECT id , name , age , <br /> COUNT(*) AS n, <br /> (<br /> SELECT id , name , age , <br /> sex <br /> FROM table2<br /> WHERE <br /> id = 2<br /> ) AS sid, yyyy <br />FROM table1<br />WHERE <br /> id = 1; | | select id, name, age, sex, ss, yy from table1 where id=1 and name=1 and a=1 and b=2 and c=4 and d=3; | SELECT id , name , age , <br /> sex , ss , yy <br />FROM table1<br />WHERE <br /> id = 1<br /> and name = 1<br /> and a = 1<br /> and b = 2<br /> and c = 4<br /> and d = 3; | | ALTER TABLE t_order ADD column4 DATE, ADD column5 DATETIME, engine ss max_rows 10,min_rows 2, <br />ADD column6 TIMESTAMP, ADD column7 TIME; | ALTER TABLE t_order<br /> ADD column4 DATE,<br /> ADD column5 DATETIME,<br /> ENGINE ss<br /> MAX_ROWS 10,<br /> MIN_ROWS 2,<br /> ADD column6 TIMESTAMP,<br /> ADD column7 TIME | | CREATE TABLE IF NOT EXISTS <br />`runoob_tbl`(`runoob_id` INT UNSIGNED AUTO_INCREMENT,`runoob_title` VARCHAR(100) NOT NULL,<br />`runoob_author` VARCHAR(40) NOT NULL,`runoob_test` NATIONAL CHAR(40),<br />`submission_date` DATE,PRIMARY KEY (`runoob_id`))ENGINE=InnoDB DEFAULT CHARSET=utf8; | CREATE TABLE IF NOT EXISTS `runoob_tbl` (<br /> `runoob_id` INT UNSIGNED AUTO_INCREMENT,<br /> `runoob_title` VARCHAR(100) NOT NULL,<br /> `runoob_author` VARCHAR(40) NOT NULL,<br /> `runoob_test` NATIONAL CHAR(40),<br /> `submission_date` DATE,<br /> PRIMARY KEY (`runoob_id`)<br />) ENGINE = InnoDB DEFAULT CHARSET = utf8; | | INSERT INTO t_order_item(order_id, user_id, status, creation_date) <br />values (1, 1, 'insert', '2017-08-08'), (2, 2, 'insert', '2017-08-08') ON DUPLICATE KEY UPDATE status = 'init'; | INSERT INTO t_order_item (order_id , user_id , status , creation_date)<br />VALUES<br /> (1, 1, 'insert', '2017-08-08'),<br /> (2, 2, 'insert', '2017-08-08')<br />ON DUPLICATE KEY UPDATE status = 'init'; | | INSERT INTO t_order SET order_id = 1, user_id = 1, status = convert(to_base64(aes_encrypt(1, 'key')) USING utf8)<br /> ON DUPLICATE KEY UPDATE status = VALUES(status); | INSERT INTO t_order SET order_id = 1,<br /> user_id = 1,<br /> status = CONVERT(to_base64(aes_encrypt(1 , 'key')) USING utf8)<br />ON DUPLICATE KEY UPDATE status = VALUES(status); | | INSERT INTO t_order (order_id, user_id, status) SELECT order_id, user_id, status FROM t_order WHERE order_id = 1; | INSERT INTO t_order (order_id , user_id , status) <br />SELECT order_id , user_id , status <br />FROM t_order<br />WHERE <br /> order_id = 1; |
原创文章,需联系作者,授权转载
上一篇:数据分片内核剖析——序章
下一篇:数智化浪潮之中,传统企业如何抓住转型机遇?
Apache ShardingSphere
文章数
96
阅读量
231327
作者其他文章
01
突破关系型数据库桎梏:云原生数据库中间件核心剖析
数据库技术的发展与变革方兴未艾,NewSQL的出现,只是将各种所需技术组合在一起,而这些技术组合在一起所实现的核心功能,推动着云原生数据库的发展。 NewSQL的三种分类中,新架构和云数据库涉及了太多与数据库相关的底层实现,为了保证本文的范围不至太过发散,我们重点介绍透明化分片数据库中间件的核心功能与实现原理,另外两种类型的NewSQL在核心功能上类似,但实现原理会有所差别。
01
Apache ShardingSphere数据脱敏全解决方案详解(上)
Apache ShardingSphere针对新业务上线、旧业务改造分别提供了相应的全套脱敏解决方案。
01
Shardingsphere整合Narayana对XA分布式事务的支持(4)
ShardingSphere对于XA方案,提供了一套SPI解决方案,对Narayana进行了整合,Narayana初始化流程,开始事务流程,获取连接流程,提交事务流程,回滚事务流程。
01
从中间件到分布式数据库生态,ShardingSphere 5.x革新变旧
5.x 是 Apache ShardingSphere从分库分表中间件向分布式数据库生态转化的里程碑,从 4.x 版本后期开始打磨的可插拔架构在 5.x 版本已逐渐成型,项目的设计理念和 API 都进行了大幅提升。欢迎大家测试使用!
最新回复
丨
点赞排行
共0条评论
Apache ShardingSphere
文章数
96
阅读量
231327
作者其他文章
01
突破关系型数据库桎梏:云原生数据库中间件核心剖析
01
Apache ShardingSphere数据脱敏全解决方案详解(上)
01
Shardingsphere整合Narayana对XA分布式事务的支持(4)
01
从中间件到分布式数据库生态,ShardingSphere 5.x革新变旧
添加企业微信
获取1V1专业服务
扫码关注
京东云开发者公众号