OB君:本文是 “OceanBase 2.0 技术解析系列” 的第八篇文章,今天我们来说说2.0版本最标志性、最不得不提的新特性——存储过程。在为数不多的原生分布式数据库中,OceanBase 2.0是第一款支持存储过程功能的产品。本文将为你深入剖析2.0中存储过程的功能特性和实现机制。更多精彩欢迎关注OceanBase公众号持续订阅本系列内容!
引言
PL/SQL(存储过程)是一种程序语言,叫做过程化SQL语言(Procedural Language/SQL),从Ada语言发展而来。PL/SQL是关系数据库对SQL语句的扩展,在普通SQL语句的基础上增加了编程语言的特点,把数据操作和查询语句组织在PL/SQL的过程化代码中,通过逻辑判断、循环等操作实现复杂的功能。
使用PL/SQL可以编写具有很多高级功能的程序,能够把业务逻辑封装在数据库内部,提供更好的抽象和安全性,同时减少了网络的交互,并且调用更快,从而提升整体性能。
目前无论是商业数据库还是开源产品几乎都在一定程度上支持类似PL的功能,但是采用的规范标准不尽相同。使用最广泛的是Oracle的PL/SQL,最早在1989年发布,语法基于Ada语言。
除此之外还有SQL Server的T-SQL,PostgreSQL的PL/pgSQL等。SQL标准中的SQL/PSM(SQL/Persistent Stored Modules)定义了存储过程中使用的PL,但是Oracle的PL/SQL已经成为了事实上的规范。MySQL采用的是SQL/PSM标准,但是并非100%兼容。OceanBase 2.0当前的PL以兼容MySQL为主体,MySQL没有的功能参照Oracle实现。
MySQL和PostgreSQL的PL都采用解释执行方式,从实现角度来说,解释执行比编译执行更容易实现,也更易于控制。但是理所当然的,解释执行比编译执行性能相对较差。
Oracle 9.x之前的PL也是解释执行(Interpreted Compilation)的,从9.x开始,Oracle提供了编译执行(Native Compilation),SQL/PL首先被翻译为C语言代码,然后调用C编译器编译为机器码,编译后的程序存在动态链接库中,运行时执行器直接装载调用动态链接库中编译好的函数。
根据Oracle自己的说明,编译后的程序比解释执行性能提升1.05到2.4倍。具体提升的程度取决于程序的特性。因为SQL语句本身还是解释执行的,所以如果PL的主体是程序控制逻辑,那么提升效果会很明显,而如果PL的主体是SQL语句,那么PL的性能提升不会显著。另外Oracle的PL调试功能只支持解释执行方式。
编译执行是一个趋势,目前除了PL之外,很多数据库也纷纷在尝试把SQL引擎改写为编译执行,包括MemSQL、HANA、PostgreSQL等。OceanBase PL采用编译执行的方式,使用LLVM生成native code,并直接把机器码装载到程序段执行,无需依赖外部C编译器和生成动态链接库。
无论解释执行还是编译执行,当前的商业数据库都只支持单机,OceanBase在分布式环境下在关系数据库里支持了完整的PL/SQL功能,这在业界尚属首次。
价值和意义
1. 降低RT
PL对于业务最直接的价值就是降低业务的RT。假如某个业务逻辑里面一条完整的执行链路平均有200条SQL,也就是应用需要和数据库交互200次。在同机房的情况下网络开销大约需要0.5ms,如果把200次交互省掉,那么就可以节约100ms的RT。当然,事实上因为业务有自身的逻辑,不可能全部逻辑都可以用PL/SQL来实现,所以这200次交互不可能全部省掉,但是即便省下一半,也会有大约50ms的RT提升。
2. 节省资源
减少网络交互除了减少RT,另一个直接的效果是减少了网络的数据传输和网络开销。假如某个互联网业务的网络开销平均是30%,也就是说30%的CPU都花在了网络的收发和解析上。如果说几十个ms的RT对整个链路来说比例不那么显著的话,30%的CPU资源节省缩省下的机器可是以亿计的真金白银,而把这些机器资源换算成系统处理能力也将是惊人的。
3. 提高吞吐率
事实上利用PL/SQL带来的网络开销的节省只是表面,更深层次的是,因为每个事务的RT缩短,每个事务在数据库内核所抢占临界区的时间缩短,使得系统的CPU利用率获得大幅提升,整个吞吐量也随之提高。这种提升不止在数据库内核存在,对业务的应用逻辑同样有帮助。CPU利用率的提升所获得价值同样也是亿级规模的。
4. 提升稳定性和可靠性
当前的互联网分布式业务多是单元化部署,应用和数据库部署在同机房,并且在大多数情况下都是机房内访问,一旦发生故障导致某个数据库的数据不可访问,OceanBase会自动切换访问其他副本,与此同时应用也必须随之切走,否则同机房的数据库访问将变成跨机房甚至跨城访问,网络开销也将由原来的每次0.5ms变成2ms,甚至达到7ms~30ms。对于单次来说这不算什么,但是在几百条SQL的加成下就可观了。
巨大的网络延迟会对整个业务系统造成严重影响,甚至拖垮整个系统。而利用了PL/SQL之后,这些问题都将不复存在,业务将不再和数据库强绑定,计算和计算的分离成为可能,整个系统的可靠性获得提升。
同时,OceanBase的设计对于网络要求相对比较高,集群内部通信以及内部流程逻辑也都可以利用PL/SQL实现,这不仅提升能够内部通信的效率,同时也使得数据库内核的核心代码充分简化,减少出错路径,提升稳定性。
并且部分业务逻辑改为PL后,业务的代码将会减少,业务开发流程也会相对简化,同时提供更好的抽象和安全性。
实现机制
1. 框架结构
图1给出了OceanBase PL Engine工作的调用关系图。PL引擎(PL Engine)和SQL引擎(SQL Engine)可以互相交互,SQL可以直接访问PL引擎,比如在一个SQL语句中使用了用户自定义函数。PL引擎可以通过SPI接口访问SQL引擎,比如在PL里进行表达式计算和执行SQL语句。
图1:PL Engine工作调用关系
2.PL Engine
PL Engine由六个模块组成:Parser、Resolver、Code Generator、Compiler、Executor和PL Cache,其中Parser、Resolver、Code Generator和Compiler构成了一个完整的PL编译流程。如图2所示。
- Parser
语法解析器,用于分析PL语法生成语法树(ParseTree)。PL引擎和SQL引擎各自实现了单独的Parser,但是两个Parser尽量不做冗余的工作。一个查询串进入Observer首先进入PL Parser解析,如果发现是SQL语句则交由SQL引擎解析。 - Resolver
Resolver用于进行语义分析,例如检查变量作用域、静态SQL中数据对象的Schema等,并为每一条PL语句生成对应的AST结构以及全局的FunctionAST结构。FunctionAST存储了PL定义的基本信息和生成的全局符号表、全局标签表、全局异常表等信息。每一条语句的AST里面则记录了指向这些全局表的逻辑信息。 - Code Generator
对于解释执行来说,AST已经足够。但是对于编译执行,需要对AST进行进一步的翻译,这一步是使用LLVM提供的接口,把AST树翻译成IR中间码的过程。IR码可以输出,以核对检验翻译过程是否正确。 - Compiler
通过JIT把IR码生成机器码的过程,输出为PLFunction结构。 - Executor
PL的执行器,根据编译出的PLFunction,和输入参数构造执行环境,调用函数指针,得到函数结果。 - PL Cache
PL Cache存在的目的是为了避免每一次都重新编译PL,提升PL的执行效率。所以对于匿名块(Anonymous Block)没有Cache的必要。
PL Cache是一个hash表,通过Key(Procedure或Function的ID)查找到Value(PLFunction),并通过访问Schema检查PLFunction的有效性,如果失效则趁机删除。
PL Cache是PL Engine的内部机制,PL Engine对外提供统一的通过ID执行Procedure或Function的接口,外部不必关心PL的缓存机制,只需通过PL Engine对象执行PL即可。
图2:PL Engine模块组成
PL Engine根据ID在PL Cache查找是否有编译好的PLFunction,如果有则进一步检查Version是否可用。如果PL Cache中没有可用的PLFunction,则调用编译流程进行编译,编译后的结果缓存在PL Cache,并交给Executor执行。
最终编译出的结果是一段二进制代码的内存地址,Executor将这段内存地址转成一个函数指针,并传入执行参数和执行环境运行得到结果。
3.编译执行
编译执行比解释执行的优势毋庸赘言,OceanBase 也是采用把存储过程编译为机器码的方式进行执行,但是和Oracle实现不同的是,OceanBase采用JIT技术,无需依赖外部C编译器和生成动态链接库。采用这一技术方案,有如下优点:
- 无需研究体系结构相关的复杂的机器码生成,就可以获得编译执行的效果;生成LLVM IR比生成汇编简单很多,且自然拥有了考虑跨平台能力;
- LLVM提供的成熟的优化模块都是基于LLVM IR的,clang等项目中对优化器的改进都可以直接被我们受益;
- 用JIT技术在Observer内直接控制生成编译后的代码,比Oracle采用的在数据库外部编译器编译结合动态链接库装载的方式在可控性,性能,可维护性各方面都有优势;
- PL的编译执行可以和SQL表达式及部分physical operator甚至是整个查询计划采用编译执行相结合,获取整体性能的提升。
图3给出了PL 编译的工作流程。Parser、Resolver和Code Generator三个模块完成了编译器的前端(Frontend)工作,实现从PL文本到中间码(LLVM IR)的转换过程。Compiler模块完成了Pass和后端(Backend)的工作,把中间码经过Pass流程优化,并生成实际的机器码。
图3:PL编译工作流程
这种架构下,对于实现PL/SQL语言来说,大量的工作是实现一个前端,即Parser、Resolver和Code Generator三个模块,而对于Compiler可以调用LLVM提供的接口实现。
兼容性
各大数据库支持的PL都各有不同。目前最广泛使用的Oracle PL/SQL语言最早在1989年发布,语法基于Ada语言。PostgreSQL的PL/pgSQL,SQL Server的T-SQL等语言都各不相同。SQL标准中的SQL/PSM (SQL/Persistent Stored Modules)定义了存储过程中使用的PL,但遗憾的是主流数据库厂商并没有全心支持。
像PL/pgSQL一样,MySQL采用了SQL/PSM标准,但是并非100%兼容。OceanBase 提供MySQL和Oracle两种兼容模式,PL/SQL也同样既可以使用Mysql的用法,也可以选择Oracle兼容模式,基于Mysql或者Oracle的PL的代码可以一行不改的迁移到OceanBase运行。
分布式
OceanBase天然的分布式特性决定PL的执行也是分布式的,PL的解析、编译和执行都在某一个Observer上完成,当PL里涉及到SQL交互时,会通过SPI调用SQL Engine,由SQL Engine执行SQL语句,如果该SQL语句是一个分布式的,那么自然而然会进行分布式执行。
OceanBase选择存储过程里访问的第一张表的数据所在的节点编译和执行PL,这是为了尽量使得PL对数据的访问都是LOCAL的。如果无法确定所访问数据所在的节点,则随机选取一个节点。
当分布式执行的SQL调用了PL函数时,PL函数可能会在多个Observer上编译执行,每个Observer上保证在相同的环境参数下进行编译,从而编译出的PL具有相同的行为。
调试
调试是编程语言的基本需求,所以事实上Mysql这样不支持调试的PL/SQL是不具备大规模应用的基础的。
业界实现PL的调试功能都只在解释执行模式支持,Oracle的编译执行模式不支持调试。OceanBase只有一种编译执行模式,支持在编译模式进行调试,并且是在分布式的环境下进行调试。目前支持的调试功能包括设置断点(b)、查看断点(info b)、查看变量(p)、查看堆栈(bt)、单步运行(s)、进入函数(s)、继续(c)等等,通讯方式采用类似Oracle的方式,建立一个数据库连接来调试另一个连接,调试指令用文本指令,不发明新的通讯协议,并通过包的形式对外提供接口。
分布式调试是分布式环境下实现PL/SQL的难点,当PL分布式执行时,用户的调试线程连到其中一个节点,该节点需要再和其他分布式执行节点建立调试连接并传输指令和数据。
包
Mysql没有包(Package),OceanBase提供和Oracle完全兼容的包机制,用户在Oracle上创建的包可以一行不改的迁移到OceanBase上创建和运行。
但是Oracle的系统包目前OceanBase还没有全部支持,这也将成为今后一段时间我们要做的工作之一。在有了PL/SQL的基础能力支持和Package的机制之后,其余的系统包可以根据需要快速实现。
总结和展望
从OceanBase诞生的第一天起,团队的目标就不是想做一个数据库只给自己用,而是要做一个通用数据库真的去推动整个社会的进步,能够让整个社会的生产力发生变化。
随着蚂蚁金服的开放赋能,OceanBase也开始服务外部客户,金融行业的很多客户以前都有很大一部分业务是使用Oracle开发,并且大量使用了PL/SQL,这种现状在非金融行业中更为普遍,而这也成为了Oracle绑在客户身上最重要的一条锁链。OceanBase要为客户提供比Oracle更可靠、更高性价比的服务,PL/SQL是必须迈过去的一道门槛。这道门槛很难迈,而一旦迈过去,就会形成OceanBase的重要优势。
另一方面,虽然互联网公司目前的现状是普遍都不使用PL/SQL进行业务开发,但是难掩其对业务的巨大价值,PL不仅能够降低RT,提高资源利用率,提升系统吞吐率、稳定性和可靠性,同时也是服务用户,尤其是传统行业客户的重要基础。在这一方面蚂蚁金服走在互联网行业前列,已经开始在内部尝试利用PL/SQL的优势来解决业务的问题。
OceanBase提供了同时兼容Mysql和Oracle两种模式的PL/SQL,并利用LLVM实现其编译执行的内核引擎,使得PL控制逻辑本身性能能够达到Oracle相当的水平。而且OceanBase是业界第一个真正意义上支持分布式PL的商业产品,并提供完善和兼容的调试机制。与此同时OceanBase还提供了完备的包机制。
未来除了进一步完善Oracle兼容的系统包之外,还需要继续优化PL/SQL的性能。与此同时,我们也会在更多内部业务中推广使用PL/SQL,为业务创造巨大的价值,并在提供给外部客户使用之前做好技术沉淀和产品锤炼。
加入OceanBase 2.0技术交流群
— 想了解更多OceanBase 2.0新特性?
— 想与蚂蚁金服OceanBase的一线技术专家深入交流?
扫描下方二维码联系小编,快速加入OceanBase技术交流群!