写点什么

如何高效使用 YashanDB PL 语言?这 5 点建议值得收藏

作者:YashanDB
  • 2024-07-11
    广东
  • 本文字数:4965 字

    阅读完需:约 16 分钟

01 背景

PL 语言(Procedural Language) 是一种程序语言, 又称过程化结构查询语言,它是一种建立在普通 SQL 语言之上的编程语言。


讨论 PL 语言是一个有意思的话题,因为软件行业被大数据、组件化的思想浪潮捶打过多年,所以多数从业人员一旦提及 PL 语言,都会在心里冒出一句疑问,“那不是已经过时的语言?”


那为什么 YashanDB 还要选择开发 PL 语言?正所谓仁者乐山,智者乐水,笔者的观点是:在技术领域,没有绝对的对错之分,关键在于是否合适。物竞天择,在软件行业,最合适应用业务实现的技术会用行动来投票决定。


如电信、证券、银行等传统行业,强调业务的高并发和高可用,通过 PL 语言来实现业务应用逻辑是主流选择。而在互联网行业,选择 PL 语言来实现业务逻辑的几乎绝迹,替代的是,业务广泛通过各种 CRUD 技术中间件 + JDBC,将业务的逻辑实现转移到数据库之外。


PL 语言之所以成为很多业务的选择,在于其独特的特性价值:

  • PL 语言具备高级语言的可编程属性,支持面向过程、面向对象的编程实现,具备业务逻辑直接在数据库中实现的条件;

  • 区别于不同组件间交互方案,大大减少了网络传输开销,明显降低业务功能端到端实现复杂度,提升业务稳定性和可靠性;

  • 高度集成 SQL,在数据库批量 SQL 操作下具有明显的性能优势;

  • Oracle 兼容性重要组成,在传统行业大量存量业务的迁移时,PL 语言的兼容性成为降低成本的关键因素。


因此笔者认为除非发生巨大的技术变更,PL 语言特性将在相当长的一段时间内会持续保持很强的竞争力。


在之前一篇论述 Oracle 兼容性的文章中,笔者以整个数据库视角来论述过国产数据库在这个方面应该达到的 4 个标准。(可点击此处查看文章:《论Oracle兼容性,我们需要做什么?》当这个范围缩小到 PL 语言时,Oracle 兼容性的视角该如何呈现呢?



Oracle 兼容性是目前国产数据库最主要的一个工作任务,这个任务直接决定了数据库在做商业迁移时的成本,是商业竞争力的一个重要指标。类比于 SQL 语言,在 PL 语言视角上也做了四层划分。


  • 第一层要求是做到 PL 语言的语法完全兼容,即 Oracle 实现的 PL 语言元素,如变量定义、函数定义、循环、控制、SQL 调用、函数调用、异常语句等,从语法格式上完全对应;

  • 第二层要求是做到 PL 语言的语义完全一致,即各类语法承载的语义要求,还有块、数据区、语句区、子过程、异常处理等,从语义实现上完全对应;

  • 第三层要求是做到 PL 语言的高级特性相同,包括承载 PL 语言的对象,包括匿名块、过程、函数、触发器、高级包等在数据字典管理、触发机制、工作原理上全面兼容;同时提供好主流的 Oracle 系统自带的高级包功能,避免存量业务代码的修改;

  • 第四层要求是做到 PL 语言的生态支持,如 PL 语言要具备易用的调试工具、承载安全特性的 PL 语言加密工具等,实现生态上的完整。


Oracle 兼容性不是一个简单的模仿行为,而是一个非常复杂和工程量庞大的逆向工程。目前国产数据库应该秉持务实的态度,专注于核心功能的实现和完善,同时确保高度的兼容和已实现功能的稳定性。


YashanDB 作为一款全自研的数据库,从 Oracle 兼容性角度上技术具备一定优势。经过几年不断的打磨,在 PL 语言特性上已初露头角。目前已推出的 YashanDB 数据库版本 PL 特性具备以下优点:

  • 高度 SQL 集成;

  • 完整的可编程逻辑;

  • 高性能;

  • 便捷的可调试性。


以下将围绕上述优点,展开描述 YashanDB PL 语言实现范围。


02 YashanDB PL 语言优点

高度 SQL 集成



上图为 YashanDB PL 语言特性实现架构。图示可见,PL 引擎与 SQL 引擎在层次上是完全解耦的,通过 SQL 引擎绑定参数特性完成 SQL 语句的编译和执行,SQL 产生结果集通过 sender 接口输出。通过缓存机制,完成 PL 和 SQL 编译体的缓存,便于使用时可以快速执行。在此架构下,PL 语言是可以完全发挥 SQL 引擎支持的所有能力。


PL 语言与结构化查询语言 SQL 的结合非常紧密,具体表现在以下方面:

  • 允许静态 SQL 操作,即直接使用所有的 DQL、DML 数据操作,事务控制语句,语句中完全支持内置函数、高级包的子函数、运算符和伪列;

  • 允许通过动态 SQL 方式进行所有 SQL 操作。该特性主要是由静态 SQL 支持范围进一步放开了 DDL 语句特性;

  • 完全支持 SQL 中定义的所有数据类型,包括数值、字符串、RAW、BOOLEAN、大对象等数据类型;

  • 支持游标变量,提供了灵活的游标 OPEN、FETCH、CLOSE、赋值、入参、出参、提前返回结果集等操作 SQL 的能力;

  • 支持 %TYPE,%ROWTYPE 等类型继承能力,而无需显式指定该数据类型

  • 运行 DQL 查询时普通游标将会一次处理查询结果集的一行,BULK 游标可以支持一次处理一批

  • SQL 操作产生异常时,均可以通过异常模块的编程进行捕获


完整的可编程逻辑



SQL 语言是一种数据描述语言,PL 语言则是极大的扩展了数据库的可编程逻辑。PL 语言源于元老级的 Ada 语言,SQL/PSM 为业界标准,Oracle 的 PL/SQL 语法形式为当前事实标准。可以实现面向过程、面向对象两种编程形式。


  • 基础要素为语句块 BLOCK,可以分为数据区和语句区两部分。多个语句块顺序或叠加,在运行时形成一种栈式的调用;

  • 数据区支持类型定义、变量定义、缺省表达式声明、异常变量定义、子过程定义等功能;

  • 除了支持 SQL 所有的数据类型,可以支持自定义类型,可定义数组、OBJECT、NEST TABLE 等向量形式,类型可支持对应的方法。通过 UDT 可支持面向对象方法编程;

  • 语句区提供了循环、条件、跳转、SQL 调用、函数调用、异常处理等可编程语言逻辑;

  • 在对象持久化层面提供了存储过程、自定义函数、触发器、自定义高级包、匿名块等多种数据库对象形态,提供了不同的触发时机和持久化机制,适用于不同的使用场景;

  • YashanDB PL 语言完全遵循 Oracle 兼容性,以 Oracle 实现的 PL 特性移植修改代价小。


高性能

PL 语言通过可编程逻辑和 SQL 集成,可以带来以下明显的好处:

  • 高效地数据批处理;

  • 显著降低客户端和数据库服务端的交互次数;

  • 减少网络流量损耗;

  • 数据库可实现的业务逻辑能力;

  • 提高业务处理的可靠性。


便捷的可调试性

使用 PL 语言程序很大阻力在难以调试 PL 语言程序。YashanDB 支持断点、STEP INTO、STEP OUT、变量查看、调用栈查看、源码查看等功能。极大方便用户进行 PL 语言的跟踪定位。


与业界常见的调试器实现方案不同,YashanDB 实现了一个轻量级的 DEBUGGER,不需要组织不同的调试语句,不存在查看会话看板,不需要去 ATTACH 操作;一键启动调试,使用复杂度大大降低。


YashanDB PL 语言的调试器特性,近期即将发布。


03 如何高效地使用 YashanDB PL 语言

笔者从基于 PL 语言开发者的角度,给出若干条建议如下:

  • 根据业务应用选择合适的 PL 对象,确保 PL 对象的规模适中;

  • 根据业务处理逻辑选择高效地语句;

  • SQL 查询相关的 PL 特性选择;

  • 减少对象的级联调用,合适的使用递归或嵌套调用;

  • 减少在线 DDL 操作,避免失效。


以下根据提供建议,进行逐个展开。


建议 1:根据业务应用选择合适的 PL 对象,确保 PL 对象规模适中

如下表,笔者给出 PL 语言对象推荐使用场景和高效使用建议,提供大家参考。


建议 2:根据业务处理逻辑选择简洁高效地语句

这个章节,笔者将通过举 1 个游标特性相关例子给大家一个直观的感受:

DECLARE	cursor cur1 is select column1 c1 from table1;  result cur1%rowtype;BEGIN	 open cur1;   loop   		fetch cur1 into result;   		exit when cur1%notfound;   		process(result);   end loop;   close cur1;END;/
复制代码


其实通过例子的分析,这个特性是完成游标的遍历,所以实际上选择 FOR 语句,可以更为简洁的完成相应功能。改写后例子如下:

BEGIN   	for result in (select column1 c1 from table1) then  	process(result);  end for; END; /
复制代码


是不是整体简洁度高了很多?实际在 PL 语言中提供了很多逻辑行语句,语句间并不存在好坏,需要从业务逻辑角度选择合适的语句去实现。


建议 3:SQL 查询相关的 PL 特性选择

PL 语言中常见的使用 SQL 的方式,有静态 SQL 特性、游标、动态 SQL 等。常见的业务逻辑是通过 SQL 获取数据后,需要进一步加工处理,然后返回处理后结果。


建议优先选择静态 SQL 特性,有以下原因:

  • 相对于动态 SQL,PL 编译器是感知静态 SQL 语句,有错误将在编译期就指出;

  • 静态 SQL 语句编译完成后,可以被 PL 编译体引用,执行阶段不需要触发编译,这样执行更为高效;

  • 静态 SQL 语句可以使用隐式游标属性来获取 SQL 执行状态。


其次推荐使用游标特性。游标是非常灵活的查询方式,而且存在多种游标形态,常用的为显式游标和系统游标。但相对于其他 SQL 查询特性,游标是需要变量形式承载,额外占用变量资源,同时遵循 PL 语言中变量的栈生命周期管理。


最后再是使用动态 SQL 特性。动态 SQL 适用于资源动态生成、动态拼接 SQL 语句和执行 DDL 语句,PL 编译阶段难以检测的,需要到执行阶段进行编译执行,灵活度高但执行效率较低。


以上是三种常见的 SQL 查询相关 PL 特性比较,当然 PL 特性没有绝对的选择好坏,只有合适业务逻辑实现的才是最好的。


建议 4:减少对象的级联调用,合适的使用递归或嵌套调用

合理规划的函数调用,可以减少编译复杂度。如下举例,给了一个较为复杂的嵌套调用,从调用关系上形成了一个有向环图。

CREATE FUNCTION F1 RETURN INT IS	result INT := 1;BEGIN	result := F2();  result := result + F3();  result := result + F1();  return result;END;/ 
CREATE FUNCTION F2 RETURN INT IS result INT;BEGIN   result := result + F4();   return result;END;/
CREATE FUNCTION F3 RETURN INT IS result INT;BEGIN   result := result + F2();   result := result + F4();   return result;END;/
CREATE FUNCTION F4 RETURN INT IS result INT;BEGIN   result := result + F1();   return result;END;/
复制代码


一个有向环图按级联编译方式,通过深度遍历优先方式进行编译时,会优先按调用链寻找叶子节点,级联展开编译。


如果深度过深,会使得编译链过深,占用大量编译资源。如图所示,我们在进行级联编译时,针对递归、嵌套、相同编译流程多次重复调用的函数等各种情形,会进行检测并及时剪枝。


当然笔者认为函数调用不可避免会出现递归和嵌套调用的情形出现,所以选择如何在合适的时机选用递归和嵌套调用,这是编程关键。但不可以滥用,必须有合适的退出条件,避免对资源产生极大损耗。


建议 5:减少在线 DDL 操作,避免失效

如果有一个数据库对象的编译使用了另一个对象的元数据信息,两个对象间就存在了依赖关系。如果被依赖对象发生了元数据信息一旦发生变更(元数据变更发生,即产生了 DDL 操作),依赖对象编译信息就失效了。


因为 PL 对象常见的实现逻辑,是封装大量的 SQL 调用,PL 对象调用等,所以一个 PL 对象会产生大量的依赖对象。当依赖对象发生 DDL,比如一个表动态增删了列,那么依据这个表的查询绑定的游标,其继承属性可能就会发生变化。再举一个例子,比如实现了一个自定义公共的字符串替换函数,当这个函数的实现发生变更,那么所有依赖这个公共函数的 PL 对象、SQL 语句等都应该发生失效重编译的动作,否则原编译结构中包含的实现逻辑就是错误的。


在 PL 对象实现时,会根据 PL 对象的依赖关系构造依赖链。如果一个对象发生元数据变更,那么这个依赖链上所有的对象都会被病毒似的传染失效。如果有大量的对象失效,那么在调用时可能会产生大量的重编译动作,极端情况会导致资源的突发损耗,甚至耗尽报错,性能也会产生极大的波动。


所以建议一个 PL 对象的依赖对象适当要控制规模,而且通过预先执行 DDL 方式,确保缓存中编译体有效。如果必须有 DDL 操作,那么建议在 DDL 操作完成后,通过 ALTER RECOMPILE 的命令,将 PL 对象提前编译为有效状态。


此外在 YashanDB PL 语言实现过程中,我们发现这种问题,也及时做出了一些应对措施,比如通过松耦合操作,及时剪断病毒式的传染。


尽管如此,PL 语言仍存在部分不足:

  • PL 语言的编写质量看 DBA 能力,难以用质量手段衡量;

  • PL 语言直接运行在数据库上,难以做好资源隔离,可能会影响主业务;

  • PL 语言的安全、审计、运维等多个角度对 DBA 要求比较高;

  • PL 语言在不同数据库间差异很大,难以移植。


如何有计划、有节奏地实现 Oracle 的 PL 语言特性,并能进一步克服 PL 语言的缺点,这是国产数据库在 PL 语言特性上面对的主要问题。比如第一点,PL 语言是缺乏其他高级语言的 UT 测试框架、静态检查工具、内存工具等各种开发者生态工具,此外覆盖率报告、内存泄露检查等完全缺失。


YashanDB 作为一款全自研的数据库,我们希望通过自身的努力,在不断追赶 Oracle 脚步上,可以青出于蓝胜于蓝,在已知这些缺点上做出自身的思考和努力,为用户提供更加优质、可靠的服务,展现我们的诚意与实力。

用户头像

YashanDB

关注

全自研国产新型大数据管理系统 2022-02-15 加入

还未添加个人简介

评论

发布
暂无评论
如何高效使用YashanDB PL语言?这5点建议值得收藏_数据库_YashanDB_InfoQ写作社区