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

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

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兼容性的视角该如何呈现呢?

image

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集成
image

上图为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操作产生异常时,均可以通过异常模块的编程进行捕获。

完整的可编程逻辑
image

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语言的调试器特性,近期即将发布。

image

03 如何高效地使用YashanDB PL语言
笔者从基于PL语言开发者的角度,给出若干条建议如下:

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

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

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

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

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

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

建议1:根据业务应用选择合适的PL对象,确保PL对象规模适中
如下表,笔者给出PL语言对象推荐使用场景和高效使用建议,提供大家参考。

PL语言对象

使用场景

高效使用建议

匿名块

一般适用只需要临时使用的计算脚本,或者比较简单的调用存储过程、自定义函数等其他PL对象的入口。

匿名块不适合过长。因为不持久化,所以不适合复杂逻辑,需要用户自己保存代码。

简单的逻辑判定,过程/函数调用,业务计算场景。

存储过程

将复杂逻辑用过程进行封装,比如数据清洗过程,可以通过存储过程中检查表的不合规数据,然后进行清洗后处理。

遵循公共和抽象原则,相似的功能复用公共过程。每个过程都会占用数据库的资源。

过程代码不适宜过长,要遵循编码的逻辑分层思想。

过程的形参和调用实参类型建议一致,避免隐式转换带来的问题。

过程不会产生返回值,但可以通过出参携带需要返回信息。可以被其他PL对象调用,也可以递归调用。

自定义函数

体现一个用户自定义的函数功能,也可以通过C/JAVA的外置自定义函数去调用外置的LIB库,比如用户可以封装一个ARRAY_SPLIT函数,提供返回数组形式。

同存储过程一样,要确保函数的规模和公共度。

调用点除了可以在其他PL对象,还可以直接在SQL语句使用。但不同调用点要遵循对应的约束条件。

外置语言的LIB库需确保安全可靠,通过参数传递信息,尽量减少使用驱动再次连接数据库获取数据操作。

触发器

DML的语句级、行级触发时机,适用于数据检查、审计等场景。

触发器中过于复杂的逻辑会降低DML的性能。

要遵循触发器的使用约束。

不建议通过触发器直接修改DML要操作的数据,DBA维护。

自定义高级包

进一步体现封装的思路,一个高级包相当于一个完整namespace,可以有独立的变量,函数,过程,异常等命名。适用于业务模块级的封装。

优先使用系统自带的高级包和内置函数。

高级包不适合过多。使用高级包较多情况下,需要对应扩大缓存资源,避免内存不足。

建议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执行状态。

image

其次推荐使用游标特性。游标是非常灵活的查询方式,而且存在多种游标形态,常用的为显式游标和系统游标。但相对于其他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;
/
一个有向环图按级联编译方式,通过深度遍历优先方式进行编译时,会优先按调用链寻找叶子节点,级联展开编译。

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

image

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

建议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脚步上,可以青出于蓝胜于蓝,在已知这些缺点上做出自身的思考和努力,为用户提供更加优质、可靠的服务,展现我们的诚意与实力。

相关文章
|
8月前
|
自然语言处理 调度 异构计算
阿里云产品二月刊来啦
通义万相2.1重磅开源,OpenSearch LLM智能问答版支持DeepSeek-R1系列模型,容器服务 Edge 版支持共享GPU调度能力,详情请点击阿里云产品二月刊
212 21
|
8月前
|
SQL 运维 监控
SQL查询太慢?实战讲解YashanDB SQL调优思路
本文是Meetup第十期“调优实战专场”的第二篇技术文章,上一篇《高效查询秘诀,解码YashanDB优化器分组查询优化手段》中,我们揭秘了YashanDB分组查询优化秘诀,本文将通过一个案例,助你快速上手YashanDB慢日志功能,精准定位“慢SQL”后进行优化。
|
9月前
|
运维 Kubernetes 网络协议
基于虚拟服务配置的渐进式迁移实践:Istio集群至ASM集群的平滑切换
本文介绍了从Istio+k8s环境迁移到阿里云ASM+ACK环境的渐进式方法,通过配置虚拟服务和入口服务实现新老集群间的服务调用与流量转发,确保业务连续性与平滑迁移
864 132
|
11月前
|
弹性计算 关系型数据库 测试技术
RDS通用云盘核心能力
本次实验主要体验RDS通用云盘的三项核心能力:IO加速、IO突发和数据归档。首先创建实验资源,包括RDS MySQL实例和ECS实例,耗时约5分钟。接着通过sysbench导入数据并配置安全设置。 在体验阶段,我们对比了开启和关闭IO加速及IO突发功能对RDS性能的影响,观察到QPS有显著差异。最后,通过将数据从云盘迁移到OSS中,展示了冷存层的数据归档功能,并进行RDS硬盘缩容,验证了其成本优势。整个实验过程详细记录了每一步操作,确保用户能直观感受到RDS通用云盘带来的性能提升和成本优化。
507 131
RDS通用云盘核心能力
|
8月前
|
安全 Shell API
FileCodeBox:像拿快递一样轻松分享文件
FileCodeBox 是一个基于 FastAPI + Vue3 开发的轻量级文件分享工具。它允许用户通过简单的方式分享文本和文件,接收者只需要一个提取码就可以取得文件,就像从快递柜取出快递一样简单。
263 17
FileCodeBox:像拿快递一样轻松分享文件
|
8月前
|
人工智能
Chain of Draft: 借鉴人类草稿思维让大型语言模型更快地思考
本研究探讨了大型语言模型(LLMs)在复杂推理任务中的计算资源消耗与响应延迟问题,特别是思维链(CoT)提示范式的效率局限性。为解决这一问题,研究引入了Chain of Draft (CoD) 方法论,通过生成简洁、高信息密度的中间输出,模拟人类认知过程。CoD将每步限制在五个单词以内,减少冗余表达,显著降低token消耗和计算成本,同时保持或提升推理准确性。实验结果显示,CoD在多种推理任务中表现出色,大幅减少了token使用量(仅为CoT的7.6%),缩短了响应时间,提升了LLM在实际应用中的效率与实用性。
210 14
Chain of Draft: 借鉴人类草稿思维让大型语言模型更快地思考
|
11月前
|
运维 监控 Cloud Native
一行代码都不改,Golang 应用链路指标日志全知道
本文将通过阿里云开源的 Golang Agent,帮助用户实现“一行代码都不改”就能获取到应用产生的各种观测数据,同时提升运维团队和研发团队的幸福感。
600 138
|
8月前
|
机器学习/深度学习 SQL 边缘计算
为襄阳职业技术学院最新推出的DeepSeek-R1-fix-XYTC:0908、DeepSeek-R1-fix-XYTC、Qwen-MAX-XYTC及DeepSeek-R1-XYTC多模态模型家族
本文档详细介绍了张永豪与联合库UNHub为襄阳职业技术学院开发的四款核心NLP模型架构,包括DeepSeek-R1-fix-XYTC:0908、DeepSeek-R1-fix-XYTC、Qwen-MAX-XYTC及DeepSeek-R1-XYTC。基于Transformer架构优化,各模型在响应延迟、知识检索和推理深度上各有侧重,适用于不同场景。文档提供了完整的架构参考、性能对比及使用建议,并附有API调用示例。建议每季度更新性能指标,确保最佳应用效果。
263 16
|
8月前
|
XML JSON API
Understanding RESTful API and Web Services: Key Differences and Use Cases
在现代软件开发中,RESTful API和Web服务均用于实现系统间通信,但各有特点。RESTful API遵循REST原则,主要使用HTTP/HTTPS协议,数据格式多为JSON或XML,适用于无状态通信;而Web服务包括SOAP和REST,常用于基于网络的API,采用标准化方法如WSDL或OpenAPI。理解两者区别有助于选择适合应用需求的解决方案,构建高效、可扩展的应用程序。
|
8月前
|
JSON 监控 数据可视化
Apipost SocketIO调试指南:对比Postman/Apifox的实操优势
实时通信调试工具可能已「过时」!许多主流工具如Apifox不支持SocketIO协议,导致调试效率低下。而Apipost作为国产工具,原生支持SocketIO,提供自定义事件管理、连接状态可视化等功能,极大提升了实时通信开发的效率和准确性。选择正确的工具,告别「伪实时」调试,提升团队协作与维护效率。
283 15