面向OceanBase的存储过程实现技术研究

2014-09-06 10:53周敏奇
关键词:数据库系统调用语句

朱 涛, 周敏奇, 张 召

(华东师范大学 软件学院, 上海 200062)



面向OceanBase的存储过程实现技术研究

朱 涛, 周敏奇, 张 召

(华东师范大学 软件学院, 上海 200062)

存储过程是一段被命名后保存在数据库服务器端,并预先编译好的代码,可以减少前台应用程序和后台数据库间的网络传输量. 本文主要研究基于静态语言和动态语言,两种典型的存储过程实现方法,来阐述存储过程实现的基本原理. 并基于此,进一步探讨了在OceanBase主存数据库服务器中添加存储过程模块的解决方案和技术难点.

存储过程; 数据库系统; SQL

1 背景简介

在各类以数据管理为核心的应用中,数据库通常扮演着底层存储的角色.应用服务器通过网络通信与数据库服务器进行交互,当某些任务需要多次访问数据库,应用服务器需要与数据库服务器进行多次的网络通信,并传送大量的中间结果. 这会影响应用对用户查询的响应速度. 为了解决这一问题,当前主流数据库 ,如Oracle, Mysql, DB2等,均允许用户在数据库服务器端编写程序来实现本在应用程序段完成的业务逻辑. 这段被命名后预先编译存储在数据库服务器端的代码即为存储过程[1].

1.1 存储过程简介

存储过程是大型数据库系统中,一组完成特定功能的程序集,经编译后存储在数据库中,上层应用调用存储过程只需要给出存储过程名和相应的参数即可. 这样可以减少应用程序与数据库间的数据交互次数和数据传输量. 因此,在一个以数据库为核心的应用系统中,需要跟数据库频繁交互的业务逻辑往往用存储过程来完成.

总的来说,存储过程具有以下三个优点:

(1) 预先编译,运行速度快. 如果跟数据库的交互工作是在前台的程序设计语言中完成,那么,每次跟数据库连接,程序中相应的SQL语句就要编译一次,而存储过程却可以一次编译多次运行.

(2) 前台应用程序和数据库服务器之间只传存储过程名称和参数,网络通信量少. 使用存储过程能在数据库服务器上完成全部对数据库的操作,只需返回最终结果. 从而减少了数据库服务器与应用程序之间的交互次数和数据传输总量.

(3) 对数据库访问封装,安全性高. 参数化的存储过程可以防止SQL语句的注入式攻击,并且可以将使用Grant、Deny以及Revoke等语句来完成=存储过程级别的权限管理.

然而,存储过程除了以上的三个优点以外,也存在以下的三个缺点:

(1) 程序调试困难. 由于存储过程是运行在数据库端的一段代码,并基于数据库提供的独特的编程语言来实现. 导致常用的调试工具不能直接用来调试这类代码. 因此,在开发存储过程时,通常面临调试困难的问题.

(2) 代码移植困难. 不像SQL语言,存储过程语言并没有被标准化. 不同的数据库支持存储过程的开发语言都不尽相同. 当Web应用使用的数据库系统发生变化时,在两个数据库系统上移植相应的存储过程需要重新开发和调试.

(3) 数据库负担重. 存储过程虽然减少了应用和数据库之间的交互,但同时增加了数据库的计算负担. 例如,原本对查询结果集的遍历访问是在应用层完成的,数据库只需要提供相应的查询结果即可. 但在存储过程中,数据库服务器不仅要提供查询结果,还需要完成遍历访问以及一些必要的数据加工和计算,并产生最终的结果返回给上层应用.

1.2 已有数据库系统对存储过程的支持

虽然主流的数据库管理系统都支持存储过程,但是它们支持的语法和使用的方法却有很大区别. 这是因为存储过程还没有形成统一的规范,并且存储过程在各个系统中的实现也受限于系统本身的设计. 表1列举了当前一些数据库支持的存储过程语言,其中的数据来源于各自的网站. 从中不难看出,不同数据库所采用的存储过程语言大不一样. 即便是被多个数据库同时采用的Java语言,其实际的存储过程编程接口还是会有很大区别.

表1 主流数据库系统存储过程开发语言

事实上,根据所使用的开发语言的性质,存储过程的实现可以分为两类:(1)采用静态过程语言实现,例如SQL PL,PSQL,SPL,SQL/PSM,PL/SQL[2]等;(2)采用已有的支持反射机制的动态编程语言实现,例如Java,.Net Framework语言. 在第3节和第4节,我们将以两个分别使用静态过程语言和已有动态编程语言的开源数据库中存储过程的实现来分别分析这两类实现方案.

2 存储过程支持的主要功能

存储过程是一段运行在数据库端的程序代码,能够被数据库系统动态地加载,编译和执行. 它支持数据库访问,变量定义,流程控制等功能. 一个数据库管理系统如果需要实现存储过程功能,需要考虑以下几个问题.

2.1 基本语法

存储过程需要支持的功能主要包括,变量声明、变量赋值、表达式计算、流程控制以及SQL语句执行,当然,也需要包括为了解决数据库和程序设计语言之间阻抗不匹配而引入的游标. 下面将对以上功能进行逐一阐述.

变量声明 存储过程支持的变量包括数据库本身支持的变量类型,以及表的行记录,游标等. 例如integer,numeric(5,2),varchar,tablename%ROWTYPE和cursor等. 在执行存储过程时,变量的取值,变量的类型等信息需要存储在过程的执行上下文中.

代码块定义 代码块可以用于逻辑分组或者把变量局部化为作用于一个比较小的语句组,在块内能定义临时变量,执行若干语句等.

表达式计算表达式计算需要处理包含常量和变量的表达式,需要产生正确的结果及结果类型. 表达式计算可以有单独的功能模块支持,也可以直接继承数据库提供的表达式计算功能.

变量赋值 变量赋值要求除了能够处理一般的数据类型,例如integer, varchar等,同时也要支持复合类型的赋值,如关系表中的行记录. 当然,变量赋值也应支持数据库中特有的游标类型的赋值.

流程控制 流程控制是提供编写复杂的业务逻辑所必须的功能. 主要包括条件语句、循环语句以及过程返回语句. 其中,循环语句需要支持对关系表记录的迭代.

数据库访问 其中包括执行SQL语句,使用游标等. 在这个模块,存储过程需要能够向主数据库引擎提交SQL查询,缓存SQL解析后的执行计划,定义和使用游标,将查询结果存储到变量中. 这个模块通常需要设计数据库的内部接口,供服务器端编程使用.

2.2 过程编译、执行与存储

存储过程的编译[6]发生在过程初次编译阶段和过程执行阶段. 这两部分工作分别是由过程编译模块和SQL编译模块完成. 前者主要负责诸如变量定义赋值,流程控制等内容的编译工作,而后者负责SQL语句的编译工作. 通常,这部分工作是在存储过程被首次调用时完成的. 例如当首次执行SQL语句时,数据库内核会完成对SQL的解析,并产生执行计划. 这部分编译结果会由存储过程模块缓存.

过程的执行同样可以分为两个部分:(1)由存储过程模块执行的语句;(2)由数据库引擎执行的语句. 前者需要定义和实现每类语句的执行函数,完成语句的执行. 后者需要定义访问数据库的接口,通过访问接口来完成语句执行. 返回的结果需要复制到存储过程模块执行的上下文中,并交由该模块进行下一步的处理.

与保存在数据库中其他信息一样,数据库管理系统也要保证存储过程在系统故障恢复后,依然可以正常使用. 因此,如何将过程序列化到非易失性存储器尤为重要. 根据采用的过程语言类型,当前主要有两种存储方案. 其中,第一种方案是,存储过程的源代码会存储在系统表中,编译后的目标代码存储在系统缓存中. 第二种方案是,在系统表中只记录存储过程的调用信息,例如过程名,参数信息等,而不存储过程源代码,编译后的结果存储在系统外部,当需要调用时,系统根据存储路径加载,并在系统内部缓存.

在下一节中,我们将以PostgreSQL为例,重点介绍设计自定义过程语言实现存储过程模块的机制.

3 静态语言存储过程实现机制

PostgreSQL[5,7]是一个自由的对象-关系数据库服务器(数据库管理系统),它在灵活的 BSD-风格许可证下发行. 它提供了相对其他开放源代码数据库系统(比如 MySQL 和 Firebird),和专有商用系统(比如 Oracle、Sybase、IBM 的 DB2 和 Microsoft SQL Server)之外的另一种选择.

PostgreSQL虽然支持使用内置过程语言PL/pgSQL、脚本语言、编译语言C和C++等编写存储过程. 但本节重点以PostgreSQL为例,阐述静态语言存储过程实现的原理和机制.

3.1 PL/pgSQL简介

PL/pgSQL是PostgreSQL数据库系统的一个可装载的过程语言,其语法类似于Oracle的PL/SQL. PL/pgSQL的设计目标是创建一种可装载的过程语言:(1)可用于创建函数和触发器过程;(2)为 SQL 语言增加控制结构;(3)可以执行复杂的计算; (4)继承所有用户定义类型,函数和操作符.

为了支持PL/pgSQL,Postgre需要提供该过程语言的编译功能、执行功能、存储和数据库访问功能.

3.2 编译与执行

3.2.1 词法解析与语法解析

在存储过程的编译阶段需要将过程源代码编译为系统内部指令树. 但需要注意的是,在函数内使用到的独立的SQL 表达式和 SQL 命令的编译则是在过程首次调用阶段完成,其由PostgreSQL的SQL解析模块负责编译,并产生执行计划.

正如传统的SQL一样,对过程源码的编译也需要经过词法解析和语法解析. 在PostgreSQL中采用开源软件Flex与Bison完成词法解析与语法解析. Flex进行词法分析,这需要我们设计过程语言的语法后,为其设计正则表达式来匹配源码中出现的标记(常量,数学符号,括号,中括号,变量名,保留字等),并定义规则将标记映射为内部标志符. 之后,Bison来对词法分析后的内部标志符序列进行语法分析,形成抽象语义树. 我们需要为每个语义单元设计识别规则,并为其创建一个相应的结构保存.

3.2.2 过程执行

过程的执行实际上是一个遍历语法树的过程,根据当前访问的指令,转到对应的指令函数进行执行. 图1以IF语句为例,来说明这个过程.

图1 IF语句的指令函数

对于每个语法树中的节点,均有一个对应的指令函数负责该指令的解释运行. 整个过程的执行是从指令树的根节点开始遍历,并在运行时决定执行的路径.

3.2.3 数据库访问

在PostgreSQL中,对于需要访问数据库的指令,指令函数需要访问系统提供的内部访问接口来完成数据库访问操作,例如,执行一条查询语句、插入数据或者计算某个表达式的结果. 在PostgreSQL内部,提供了服务端编程接口(Server Programming Interface)来接收来自系统内部的数据操作和访问请求. 在下表中,表2列举了接口包含的部分重要访问函数:

表2 SPI访问接口

3.3 存储策略

存储过程的存储,即是对过程的持久化,要保证过程在系统重启后依旧能够使用. 在PostgreSQL中,过程存储分为两个部分,过程的源代码和编译后的指令集.

过程的源代码会被存储在系统表pg_proc中. 该系统表用于存储关于函数或者过程的信息. 表3列举了需要存储的部分重要属性. 从表3可以看出,系统表主要存储了过程的原始信息,包括名字,源码,参数,调用方式等. 由于这些信息是存储在系统表中的,从而保证了过程存储的持久化.

过程会在创建或第一次调用的时候被编译. 编译后的执行计划会被存储在位于系统缓存中的hash表中. 每个过程对应一条记录,主键为根据过程名和参数计算得到的hash值,映射的对象是过程编译后的指令集.

对过程的调用,首先会从系统表中找到对应的过程记录,然后在系统缓存中寻找是否存在已编译的指令集,如果没有,那么根据系统表中的prosrc字段重新编译,并存储到系统缓存中.

表3 pg_proc表的部分重要属性

3.3 静态语言实现存储过程小结

PostgreSQL采用的存储过程实现方式是定制了自己的过程语言PL/pgSQL,在系统内部实现过程的编译、运行和存储. 这种策略的主要工作事实上可以分为两个部分:(1)提供过程语言的编译和运行机制;(2)设计过程语言访问数据库系统的接口. 而大量的工作在于提供过程语言的基本功能. 在下一节中,我们将介绍一种更为简明的,利用动态语言中的反射机制来实现存储过程的策略.

4 动态语言存储过程实现机制

VoltDB[8,9]是一个内存数据库[10],提供了 NoSQL 数据库的可伸缩性和传统关系数据库系统的 ACID 一致性. 使用 Java编写的存储过程进行数据操作. 我们以VoltDB系统为例来阐述使用动态语言来实现存储过程的原理,VoltDB在上层采用的是Java语言开发,负责系统的集群管理,事务调度,SQL解析,接受客户端连接等功能;下层采用的C++语言开发,负责SQL的执行和数据的存取. 存储过程实现在Java层.

4.1 反射机制

从效果来看,存储过程是一段动态添加到系统中执行的代码. 而Java提供的反射机制具备这样的功能. 反射机制允许Java在运行时加载、探知、使用编译期间完全未知的类. Java可以加载一个运行时才得知名称的类,获悉其完整构造,并生成对象实体、或修改成员变量,或调用方法.

VoltDB利用该机制,为应用程序提供了可扩展的编程接口,应用程序员通过继承系统提供的过程基类,定制应用访问数据库的逻辑,并在系统外部使用Java编译器进行编译,并最终交由系统在运行时加载和调用.

4.2 过程的编译与执行

VoltDB中存储过程的流程控制,变量定义等功能完全由Java提供,而对于需要访问数据库的操作,设计了相应的接口. 每一个存储过程都需要继承这个接口,并使用相应的接口方法来提交查询,获得结果.

下面我们给出一个VoltDB中存储过程的样例.

public class KvUdpate extends VoltProcedure {

public final SQLStmtaddRecord = new SQLStmt

“insert into kvstore values(?,?);”

);

publicVoltTable[] run(int a, int b) throws Exception{

voltQueueSQL(addRecord, a, b);

voltExecuteSQL(true);

return null;

}

}

这段代码会被Java编译器编译. 例如:

$javac -cp $“CLASSPATH:/opt/voltdb/voltdb/*”UpdatePeople.java

这里Java编译器只负责编译Java语法相关的内容. 而对于SQL语句是由VoltDB的SQL解析引擎在过程调用时负责编译和缓存.

VoltProcedure主要提供了如表4所示的接口函数:

表4 VoltProcedure主要接口函数

需要注意的是,在VoltProcedure中并没有提供游标等功能,对表的迭代访问是使用VoltTable提供的相应操作方法. 编译与执行的整体框架如图2所示.

图2 VoltDB存储过程框架

4.3 过程的存储

存储过程采用Java编写,在系统外进行编译和存储,关于过程的信息,例如过程名,参数,使用的SQL语句会被存储在系统表中. 在外部的类文件被调用后,系统内部会缓存类信息,已备下一次的调用.

编译后的存储过程会在系统表中进行注册. 例如:

CREATE PROCEDURE FROM CLASS UpdatePeople;

PARTITION PROCEDURE UpdatePeople ON TABLE people COLUMN state_num;

运行以上的命令后,系统表中就记录了这个过程的相关信息,从而方便之后的再调用. VoltDB对过程的调用是通过系统表中提供的过程名和相应的存储路径,并据此,从外部加载相应的类,通过反射机制生成该类的实例,调用相应的run()方法.

4.4 支持反射的动态语言实现存储过程小结

利用动态语言(例如,Java)的反射机制能够非常便捷地实现存储过程. 它不需要实现存储过程的编译和执行,从而大大降低了开发难度. 然而,这取决于底层数据库采用的实现语言是否能够提供反射机制,因而已有利用的动态语言来实现存储过程有一定的局限性.

5 OceanBase中的实现思路

OceanBase[3,4]是阿里集团研发的可扩展的关系数据库. 目前的版本暂时不支持存储过程的编写,本小节我们将探讨在OceanBase中添加存储过程模块的关键技术. OceanBase整机架构分为四个模块:主控服务器RootServer,更新服务器UpdateServer,基线数据服务器ChunkServer以及合并服务器MergeServer.

◆ 客户端:用户使用OB的方式和MySQL数据库完全相同,支持JDBC、C客户端访问.

◆ RootServer: 管理集群中的所有服务器,Tablet数据分布以及副本管理. RootServer一般为一主一备,主备之间数据强同步.

◆ UpdateServer:存储OB系统的增量更新数据. UpdateServer一般为一主一备,UpdateServer和RootServer一般部署在同一服务器中.

◆ ChunkServer:存储OB系统的基线数据. 基准数据一般存储两份或者三份.

◆ MergeServer:接收并解析用户的SQL请求,经过词法分析、语法分析、查询优化等一系列操作后转发给相应的ChunkServer. 如果请求数据分布在多台ChunkServer上,MergeServer还需对多台ChunkServer返回的结果进行合并.

图3 OceanBase架构

正如之前所讨论的,存储过程的模块的编写主要有以PostgreSQL为代表的静态语言过程语言的方式,以及以VoltDB为代表的基于动态语言反射机制的方式.

由于OceanBase是采用C++语言开发的,所以实现存储过程的策略主要参考Postgre的实现方案. MergerServer是OB查询的入口,因而存储过程实现在MergeServer上. 添加存储过程模块,需要增加存储过程编译模块,存储过程执行模块,Obsql对存储过程定义和调用的支持以及存储过程访问SQL引擎的接口,其基本框架如图4所示:

图4 存储过程基本框架设计

• 定义存储过程的指令首先被MergerServer接收,MergerServer调用SQL词法语法解析模块,对过程定义的部分会转到存储过程的编译模块完成.

• 编译后的结果可以缓存在系统内部,过程源代码可以存储在系统表中.

• 调用一个存储过程通过SQL查询的入口点,转到存储过程的执行模块,当遇到需要访问数据库的操作时,通过访问接口调用SQL引擎的编译或者执行模块.

在MergeServer上,需要封装存储过程访问SQL引擎的访问接口. 该接口需要提供如表5所示的基本访问功能.

表5 OceanBase的服务端编程接口需要的基本功能

6 总 结

存储过程是用户使用数据库的重要功能. 它大大改善了前台应用访问数据库的性能. 现有的主流数据库都支持这项功能. 存储过程的实现方案主要取决于数据库系统的设计和实现方案. 本文主要阐述了基于静态语言和基于动态语言的反射机制实现存储过程的基本原理. 并进一步以采用C开发的PostgreSQL和以Java开发的VoltDB为例,主要讨论了存储过程在不同系统中的实现原理. 最后,对OceanBase中实现存储过程这项功能给出了初步的设计.

[1] Stored Procedure[EB/OL]. http://en.wikipedia.org/wiki/Stored_procedure.

[2] PL/pgSQL[EB/OL]. http://www.postgresql.org/docs/8.3/static/plpgsql.html.

[3] OceanBase[EB/OL]. http://alibaba.github.io/oceanbase/.

[4] 杨传辉. 大规模分布式存储系统原理解析与架构实战[M]. 北京:工业出版社,2013.

[5] 彭智勇,彭煜玮. PostgreSQL数据库内核分析[M]. 北京:机械工业出版社华章公司,2012.

[6] AHO A V, ULLMAN J D. Principles of Compiler Design[M]. [s.L.]:Addison-Wesley, 1977.

[7] STONEBRAKER M, KEMNITZ G. The Postgres Next Generation Database Management System[J]. Commun ACM, 1991, 34(10): 78-92.

[8] KALLMAN R, KIMURA H, NATKINS J, et al. Abadi: H-store: a high-performance, distributed main memory transaction processing system[J]. PVLDB, 2008,1(2): 1496-1499.

[9] STONEBRAKER M, WEISBERG A. The VoltDB Main Memory DBMS[J]. IEEE Data Eng Bull, 2013, 36(2): 21-27.

[10] STONNEBRAKER M, MADDEN S, ABADI D J, et al. The end of an Architectural Era: (It’s Time for a Complete Rewrite)[C]//VLDB ’07: Proceedings of the 33rd International Conference on Very Large Data Bases, 2007: 1150-1160.

(责任编辑 王善平)

Study on stored procedure implementation oriented to OceanBase

ZHU Tao, ZHOU Min-qi, ZHANG Zhao

(SoftwareEngineerInstitute,EastChinaNormalUniversity,Shanghai200062,China)

A stored procedure is a pre-compiled subroutine stored in database server, which improves the efficiency of applications’ database access. This paper discussed the implementation of stored procedure based on both static language and dynamic language. Besides, we gave a primary design for implementing stored procedures in OceanBase.

stored procedure; database system; SQL

1000-5641(2014)05-0281-09

2014-07

国家自然科学基金(12345678);上海市重点学科建设项目(98776654)

朱涛,男,博士研究生,研究方向为内存数据库,分布式系统. E-mail:zhutaojs@gmail.com.

周敏奇,男,副教授,硕士生导师,研究方向为内存数据库. E-mail:mqzhou@sei.ecnu.edu.cn.

TP392

A

10.3969/j.issn.1000-5641.2014.05.025

猜你喜欢
数据库系统调用语句
重点:语句衔接
核电项目物项调用管理的应用研究
LabWindows/CVI下基于ActiveX技术的Excel调用
微细铣削工艺数据库系统设计与开发
江苏省ETC数据库系统改造升级方案探讨
基于系统调用的恶意软件检测技术研究
实时数据库系统数据安全采集方案
核反应堆材料数据库系统及其应用
如何搞定语句衔接题
利用RFC技术实现SAP系统接口通信