OceanBase 从2.0版本开始支持了存储过程(点击文章:“OceanBase 2.0——第一款支持“存储过程”的原生分布式数据库 ”回顾),并提供了MySQL和Oracle两种兼容模式。其中,2.0版本提供了较为完备的MySQL模式的存储过程功能,但对Oracle模式的存储过程功能(PL/SQL)支持还不够完全。在OceanBase 2.2版本中,我们在存储过程方面做了许多重大的改进和突破,本文将从基本功能完善、协议层的扩展和性能提升等方面介绍OceanBase 2.2版本在存储过程方面取得的新进展。
支持子过程和子类型
Collection类型的完善
在原来仅提供Nested Table一种Collection的基础上,增加了Varray和Associative Array两种类型
实现了first、last、prior、next、limit、exists方法,并完善了delete方法
支持 %TYPE 与 %ROWTYPE
游标的完善:游标变量、游标的属性、带参数游标等功能
支持自治事务
支持FORALL语句
新增系统包:dmbs_random、dbms_lob、dbms_metadata、dbms_output、dbms_spm、utl_raw
通过以上功能的补充和完善,OceanBase 2.2版本已经完成了对Oracle存储过程 90%以上的功能覆盖。在对外输出中我们发现,用户原本运行在Oracle上的存储过程代码绝大部分都可以无修改的在2.2版本的Oracle模式正常执行。
驱动扩展了MySQL Protocol Type,并增加了对复杂数据类型的set接口:setArray和setObject,根据复杂类型的序列化方法对数据进行序列化并发送给server。OceanBase对外提供兼容Oracle的视图ALL_TYPES、USER_COLL_TYPES、USER_TYPE_ATTRS、ALL_TYPE_ATTRS、ALL_COLL_TYPES等,驱动根据需要发送SQL去访问这些视图拉取需要的META信息,从而能够反序列化server发送的数据。
复合数据类型传输协议的扩展,使得从OceanBase 2.2版本开始,用户可以通过应用程序直接向server发送Array和Object数据对象,或者从server读取Array和Object数据对象,大大降低了应用开发的复杂度,简化了开发和维护的工作量,如 例1 为OceanBase 2.2版本的 TPC-C测试new order代码。否则,应用需要传输Array或者Object时,必须把结构拆成多个的基础数据类型,如 例2 为DBT2的 TPCC测试new order代码。
CREATE OR REPLACE TYPE intarray IS TABLE OF INTEGER;CREATE OR REPLACE TYPE numarray IS TABLE OF NUMBER;CREATE OR REPLACE TYPE distarray IS TABLE OF VARCHAR(24);CREATE OR REPLACE TYPE chararray IS TABLE OF VARCHAR(1);CREATE OR REPLACE PROCEDURE neworder (par_w_id INTEGER,par_d_id INTEGER,par_c_id INTEGER,par_o_all_local INTEGER,par_o_ol_cnt IN OUT BINARY_INTEGER,par_w_tax OUT NUMBER,par_d_tax OUT NUMBER,par_o_id OUT INTEGER,par_c_discount OUT NUMBER,par_c_credit OUT varchar2,par_c_last OUT varchar2,par_retry IN OUT BINARY_INTEGER,par_cr_date DATE,par_ol_i_id intarray,par_ol_supply_w_id intarray,par_i_price OUT numarray,par_i_name OUT distarray,par_s_quantity OUT intarray,par_brand_generic OUT chararray,par_ol_amount OUT numarray,par_s_remote intarray,par_ol_quantity intarray)IS BEGIN……END;
例2:
CREATE OR REPLACE PROCEDURE neworder (w_id INT,d_id INT,c_id INT,o_all_local INT,o_ol_cnt INT,ol_i_id1 INT,ol_supply_w_id1 INT,ol_quantity1 INT,ol_i_id2 INT,ol_supply_w_id2 INT,ol_quantity2 INT,ol_i_id3 INT,ol_supply_w_id3 INT,ol_quantity3 INT,ol_i_id4 INT,ol_supply_w_id4 INT,ol_quantity4 INT,ol_i_id5 INT,ol_supply_w_id5 INT,ol_quantity5 INT,ol_i_id6 INT,ol_supply_w_id6 INT,ol_quantity6 INT,ol_i_id7 INT,ol_supply_w_id7 INT,ol_quantity7 INT,ol_i_id8 INT,ol_supply_w_id8 INT,ol_quantity8 INT,ol_i_id9 INT,ol_supply_w_id9 INT,ol_quantity9 INT,ol_i_id10 INT,ol_supply_w_id10 INT,ol_quantity10 INT,ol_i_id11 INT,ol_supply_w_id11 INT,ol_quantity11 INT,ol_i_id12 INT,ol_supply_w_id12 INT,ol_quantity12 INT,ol_i_id13 INT,ol_supply_w_id13 INT,ol_quantity13 INT,ol_i_id14 INT,ol_supply_w_id14 INT,ol_quantity14 INT,ol_i_id15 INT,ol_supply_w_id15 INT,ol_quantity15 INT,out rc int)IS BEGIN……END;/
OceanBase 2.2的Oracle模式实现了服务端游标功能。服务端游标的主要作用是可以实现Client从server按需获取数据,减少数据通信量,并简化应用程序开发,如 例3 所示。而在支持这一协议前,应用只能通过Array获取SQL语句的所有结果集,当结果集非常大的时候,不仅对网络造成巨大压力,而且可能会造成驱动的内存耗尽。
例3:CREATE OR REPLACE PROCEDURE emp_cursor(p_cursor OUT sys_refcursor)ISBEGINopen p_cursor for select * from test_emp;END;/JAVA代码:CallableStatement csmt = conn.prepareCall("{call emp_cursor(?)}");csmt.registerOutParameter(1, OracleTypes.CURSOR);csmt.execute();ResultSet resultSet = (ResultSet) csmt.getObject(1);while (resultSet.next()) {int columnCnt = resultSet.getMetaData().getColumnCount();for (int j = 1; j <= columnCnt; j++) {System.out.println(resultSet.getMetaData().getColumnName(j) + ":"+ resultSet.getString(j));}}
Array Binding
Array Binding是对DML语句进行批量处理的能力,这一优化在TPC-C测试中发挥了重要的作用。一条SQL在数据库中的执行过程大致可以分为“计划生成”和“执行”两个阶段,尽管我们对SQL的执行计划做了高速缓存,但找到一个合适的执行计划在整个执行过程中仍然是比较耗时的一个部分。当一组SQL的执行计划完全一样而只有执行期参数不同是,在存储过程中我们可以通过特定的语法将他们的执行做成一个批量处理的过程,此时“计划生成”只需要做一次即可。在Array Binding中,数据库首先找到需要使用的计划,然后执行该计划,并在每次执行完毕后,重新执行参数绑定(Binding)的过程。
Array Binding这一性能优化是通过存储过程中使用FORALL语句进行触发的。使用FORALL不仅可以优化SQL语句的计划生成,还大幅减少了存储过程引擎和SQL引擎之间的交互次数。在Client和Server之间的交互通过存储过程被大幅减少之后,存储过程引擎和SQL引擎之间交互的减少带来的性能提升十分可观。如 例4 所示是TPC-C测试中delivery事务的实现代码,使用了FORALL后,原本几十次的存储过程引擎和SQL引擎之间交互、以及SQL语句的计划生成和查找过程被减少到了4次。
CREATE OR REPLACE PROCEDURE delivery (ware_id IN INTEGER,dist_id OUT intarray,order_id OUT intarray,ordcnt OUT INTEGER,sums OUT numarray,del_date IN DATE,carrier_id IN INTEGER,order_c_id OUT intarray,retry IN OUT BINARY_INTEGER)ISTYPE int_array IS TABLE OF BINARY_INTEGER;var_dist int_array := int_array();not_serializable EXCEPTION;PRAGMA EXCEPTION_INIT(not_serializable,-6235);BEGINvar_dist.EXTEND(10);FOR var_x IN 1..10 LOOPvar_dist(var_x) := var_x;END LOOP;LOOPBEGINordcnt := 0;IF dist_id.count != 0 THENdist_id.delete;order_id.delete;END IF;FORALL IDX IN 1..10DELETE FROM nordWHERE no_w_id = ware_id AND no_d_id = var_dist(IDX) AND no_o_id =(SELECT no_o_id FROM nordWHERE no_d_id = var_dist(IDX) AND no_w_id = ware_id AND rownum <= 1)RETURNING no_d_id, no_o_id BULK COLLECT INTO dist_id, order_id;ordcnt := SQL%ROWCOUNT;FORALL o in 1.. ordcntUPDATE ordr SET o_carrier_id = carrier_id WHERE o_w_id = ware_idAND o_d_id = dist_id(o)AND o_id = order_id(o)RETURNING o_c_id BULK COLLECT INTO order_c_id;FORALL o in 1.. ordcntUPDATE ordl SET ol_delivery_d = del_date WHERE ol_w_id = ware_idAND ol_d_id = dist_id(o)AND ol_o_id = order_id(o)RETURNING sum(ol_amount) BULK COLLECT INTO sums;FORALL c IN 1.. ordcntUPDATE custSET c_balance = c_balance + sums(c), c_delivery_cnt = c_delivery_cnt + 1WHERE c_w_id = ware_id AND c_d_id = dist_id(c) AND c_id = order_c_id(c);COMMIT;/* No exceptions, exit*/EXIT;EXCEPTIONWHEN not_serializable THENBEGINROLLBACK;retry := retry + 1;END;END;END LOOP;END delivery;/
Prepared Statement协议优化
表达式计算优化
因为采用了基于LLVM的编译执行实现存储过程基础框架,存储过程中的性能消耗主要发生在表达式计算和SQL执行上,真正存储过程控制逻辑的占比非常少。所以表达式计算是性能优化的重要方向。
为了实现更好的代码可维护性,存储过程的表达式计算采用的是同SQL一样的表达式计算引擎,也就是说所有存储过程需要计算表达式的时候,会通过调用SQL引擎完成。这里除了本身大量的调用开销之外,SQL引擎的表达式计算框架采用的是基于递归的中缀计算框架。存储过程里存在大量的整数比较、自加自减、数值初始化等操作,这些表达式的计算非常简单,走一遍执行一遍SQL引擎的表达式计算流程非常耗时,严重影响了存储过程控制逻辑的运行速度。为此我们对这种简单表达式实现了一套简单的基于LLVM的计算框架,并内嵌在存储过程的执行引擎当中,一方面避免了对SQL引擎的额外调用,另一方面大幅提升了表达式计算效率。根据测试,仅这一优化就使得循环控制逻辑的性能提升百倍以上。
TCL处理优化
TCL是指事务控制语句,在存储过程里主要是COMMIT、ROLLBACK。为了实现更高的性能,2.2改掉了原本通过SQL引擎执行TCL语句的方式,而是直接通过存储过程引擎调用事务层接口实现,省掉了中间十几层的调用栈消耗。
另外,在一些特殊场景,存储过程里的TCL语句可以进行异步提交,工作线程在执行到TCL语句时,不必等待事务提交完成,而是把事务提交任务交给异步线程完成,自己去接受新的请求。
OceanBase 2.2版本是全面支持Oracle PL/SQL的版本,其中的存储过程功能除了在TPC-C测试中发挥重大作用之外,在内部业务中也已经上线,经历了双11极致场景的考验,而且已经输出到了多家外部客户业务。
随着OceanBase的产品越来越成熟,存储过程将在OceanBase商业化进程中发挥越来越大的作用。尽管2.2版本的存储过程功能相比之前已经有了长足进步,但相比Oracle丰富的PL/SQL还存在很多不完善的地方,后续版本正在迅速弥补这些短板,尤其是下一阶段,我们将支持更多兼容Oracle的内建系统包,配合存储过程的功能支持,为用户业务的平滑迁移提供有力的支持。
立即申请免费体验OceanBase 2.2版本
「 OceanBase 2.2 版本 」正式上线官网啦!OceanBase 2.2版本是成功支撑2019年天猫双11大促的稳定版本,同时也是用于TPC-C测试且荣登TPC-C性能榜首的版本。OceanBase 2.2版本除了在蚂蚁金服和网商银行广泛使用外,目前也在部分金融机构中使用。 想要立即体验「OceanBase 2.2版本」? 免费获取链接: https://oceanbase.alipay.com/download/resource(或点击下方阅读原文) 如果你在安装和使用的过程中遇到问题且希望跟OceanBase一线专家进行技术交流,我们为开发者用户提供了两种渠道: 1)加入OceanBase技术交流钉钉群,打开钉钉搜索群号:21949783(备注:OB 2.2) 即可加入 2)扫描下方二维码添加OB小助手微信号(备注:OB 2.2)通过验证后拉您进入OceanBase 2.2版本交流群 我们非常重视来自每一位开发者用户的体验和心得,希望能够获得你们的宝贵反馈。
● OceanBase 2.2 版本安装体验——OCP 2.3
▼内容这么棒,还不赶紧扫码关注一下!▼
点击“阅读原文”即可免费体验OceanBase 2.2版本









