《PostgreSQL服务器编程》一一2.6 过程化语言

本文涉及的产品
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
简介:

本节书摘来自华章计算机《PostgreSQL服务器编程》一书中的第2章,第2.6节,作者:(美)Hannu Krosing, Jim Mlodgenski, Kirk Roybal 著
,更多章节内容可以访问云栖社区“华章计算机”公众号查看。

2.6 过程化语言

SQL Server允许你使用任何产生CLR的语言来创建DLL。这些DLL必须在启动的时候被加载到服务器里面。为了在运行时间创建一个程序,并且使它立即可用,唯一的选择就是内建的SQL方言——Transact SQL(TSQL)。
MySQL有一个叫做插件的功能。其中一种合法的插件类型是过程化语言。几种语言完成加工后,借由插件系统可以和MySQL一起工作。这些语言包括了最流行的几种语言,比如PHP、Python等。这些函数不能被用在存储过程与触发器中,但是它们可以被普通SQL语言唤醒。接下来,你就会被内建的SQL没完没了地纠缠。
PostgreSQL完全支持额外的过程化语言,这种语言可以被用来在数据库中创建任何合法的实体,而这些实体可以使用PL/pgSQL来创建。这些语言可以从一个正在运行的PostgreSQL版本中进行添加(或删除),而且任何的使用这种语言的函数定义也可以在PostgreSQL运行的时候被创建或者抛弃。这些语言对PostgreSQL内部函数和所有数据实体具备所有的访问权限,这样调用者是被允许的。
对于PostgreSQL,事实上有许多类似的插件语言扩展程序可供使用。我自己已经使用过的包括:PHP、Python、bash和PL/pgSQL。这意味着PostgreSQL的标准语言也需要使用其他语言所使用的相同扩展系统,来进行安装和管理。
这个让我们意识到,相比于最初所料想的,实际上有更多的开发者可以使用PostgreSQL。软件开发者不必去学习一个新的开发语言来实现存储过程。他们可以选择合适的语言来扩展PostgreSQL,并且继续按照之前的工作风格和流程进行代码
编写。
经验总结:在PostgreSQL开发社区里没有二等公民。任何人几乎可以使用任何语言进行编码。
第三方工具:对于不同的数据库平台,我们经常会比较平台上可以使用的第三方应用的数量。我不确定第三方工具的总数和你实际所需要的第三方应用数量是否同样
重要。
最后,以下是一张产品列表,我经常会将这些产品和PostgreSQL一起使用:
Pentaho Data Integration (kettle):一个优秀的抽取、转换、加载(Extract Transform and Load,ETL)工具
Pentaho Report Server:一个强大的报告引擎
PgAdmin3:一个极好的数据库管理工具
php5-postgesql:一个供PHP进行本地访问PostgreSQL的包
Qcubed:一个支持PostgreSQL的PHP开发框架
Yii:一个很好的PHP开发框架
Talend:一个有用的ETL工具,但是并不是我所喜欢的
BIRT:一个很好的Java报告工具,这个工具带有简单的报告创建环境
psycopg2:Python针对PostgreSQL的套件
以上几乎是一张完美的产品列表,这些工具已经让PostgreSQL开发变成了一件轻而易举的事情。我们可以用这样一张支持PostgreSQL的应用程序列表来充实本书内容。也非常感谢他们的合法授权,PostgreSQL可以被内嵌到很多商业应用中,但是你可能从来没有真正认识它。
经验总结:你不用过多考虑到底现有多少工具可以支持PostgreSQL这个产品。所有重要的工具都是可用的。
2.6.1 平台兼容性
SQL Server是微软的产品。这样说来,它曾经是,而且将来也会是一款微软平台的工具。通过ODBC,我们可以进行一定的限定级别的访问,但是对于跨平台的开发来说,它不是一个严谨的选择。目前来看,MySQL和PostgreSQL支持当前可用的每一个操作系统。这种能力(或者说限制的缺失)对于长期的稳定性来说是一个非常有力的因素。如果某种特定的操作系统不再可用,或者不再支持开源软件,那么把数据库服务器迁移到另外一个平台将是一件非常麻烦的事情。
2.6.2 应用程序设计
“已有的事,后必再有。已行的事,后必再行。日光之下并无新事。”
——传道书 1:8-10 KJV
“旧的事情都已经过去,看呀,所有新的事情都已经发生。”
——2哥林多后书5:16-18 KJV
在软件开发过程中,我们经常会遇到这样的情况,当过时的技术再次兴起时,这些开发者就如同信奉宗教一样拥抱这种观点。我们曾经在瘦服务端与瘦客户端之间摆动,在扁平存储与分级存储之间选择,也经历了从桌面应用到网络应用的转变,但在本章中,我们最适宜讨论的话题是客户端与服务端程序设计。
程序设计实现之间之所以会出现这种摇摆,与客户端或服务端所能提供的功能无任何关系,反而很大程度上是开发者的经验会产生更大的影响,且这种影响可以导向任何一种选择,这种选择取决于开发者首先碰见的是何种程序设计实现方式。
我鼓励服务端开发者与客户端开发者都先撇下他们所使用的工具,之后再阅读本章剩余部分。
在接下来有限的时间内,我们将讨论“服务器程序设计”的绝大多数新功能。如果那时候你仍然没有被说服,那我们会看一下,在你没有抛弃应用为中心的观点的情况下,你如何利用这些功能所带来的诸多好处。
1.数据库被认为是不利的
看待服务器程序设计的最简单、最省事的方法是把数据库看做一个数据桶。你只需要使用最基础的SQL语句,比如INSERT、SELECT、UPDATE和DELETE,就可以每次操作一个单一的数据行,而且你还可以轻松地为多数据库创建应用程序库。
这个方法有一些明显的缺陷。以每次一行的方式在数据库服务器之间移动数据,这种方式的效率极低,并且你也会发现这种方法在网络架构的应用程序中完全不可行。
这个观点经常和“数据抽象层”联系在一起,这是一种客户端库文件,它允许开发者花费较少的力气将数据库从应用程序下面分离出来。这个抽象层在开源开发社区中是非常有用的,它可以被使用在多种数据库上,并且不需要财务上的扶持就可以获得最佳的性能。
在27年的职业生涯中,在没有抛弃应用程序的情况下,我从来没有改变过任何一个应用所使用的数据库。敏捷软件开发的原则之一是YAGNI(你并不需要它)。这就是其中的一个例子。
经验总结:数据抽象对项目来说是有价值的,尤其是对于那些在安装的时候需要选择数据库平台的项目。对于任何其他人,只需要说no。
2.封装
另一个偏向于客户端开发体系的技巧是尝试将数据库中具体的调用分离到一个程序库中。这个设计的目标通常是让应用程序对所有业务逻辑进行控制。在这种情况下,应用程序仍然扮演着国王的角色,而数据库仅仅是受国王控制的一个必要的祸害。
这个数据库架构的观点揭露了应用程序开发者的短处,就比如他们忽略了一个装满了工具的工具箱而仅仅选择了那把锤子。应用程序中的所有东西到时候都被绘制得像一枚枚钉子,然后开发者可以使用锤子敲打它们。
经验总结:千万不要仅仅因为对数据库不熟悉,就放弃数据库所能带来的强大力量。使用过程化语言,检查一下扩展应用的工具包。那里有一些很棒的产品。
3.PostgreSQL可以提供什么
到目前为止,我们已经提到了过程化语言、函数、触发器、定制的数据类型和运算符。这些东西可以在数据库里面通过CREATE命令直接创建,或者使用扩展应用被添加为库
文件。
现在我将向你展示一些事情,这些事情是你在PostgreSQL的服务器上进行程序设计的时候所需要记住的。
4.数据位置
如果可以的话,尽量将数据保存在服务器上。请相信我,数据在服务器上会更加顺畅,当修改数据的时候性能会更好。如果所有的事情都在应用层完成的话,首先数据需要从数据库端返回给程序,然后进行修改操作,最后把数据发送回数据库去执行这个事务。如果你正在开发一个网络架构的应用程序,你最不该考虑的就是上述方法。
让我们来看一小段程序,来看看如何使用两种方法实现对一个记录的更新:
image

这一小段代码将一行记录从数据库服务器拉出来并推送到客户端、进行数据评估,然后基于评估结果修改客户的账户信息。修改的结果最后会被发送回数据库进行处理。
在这个应用场景中,有几个错误的地方。首先,这个架构是可怕的。想象一下如果这个操作需要被上千甚至百万级的客户执行后果会是怎么样的呢?
第二个问题是事务的完整性。如果在查询与更新语句执行之间,一些其他的事务更新了这个用户的账户,出现这种情况该怎么办?这个客户是否仍然是价值客户?这取决于评估的业务逻辑。
尝试以下的示例:
image

这个示例变得更加简单了,它考虑了事务的完整性,并且可以应对相当大数量的客户操作。为什么我们在这里展示这么一个简单且明显的例子呢?因为许多开发框架都默认地按照错误的方式。可以预见的是,为了实现跨平台以及快捷地将形式集成到简单的设计模型里面,代码生成器会产生和这个例子相等的形式。
这个方法催生了一些可怕的事实。对于一个拥有少数并发事务的系统,你可能可以看到你期待的内容,但是随着并发量的增长,意外情况的也会频发。
第二个例子展示了一个更好的想法:对列进行操作,而不是行,将数据留在服务器上,并且让数据库为你完成事务操作。这就是数据库存在的理由。
2.6.3 更多基础
在开始服务器程序设计之前,这里旨在提供一些基础的背景信息。在接下来的几部分中,我们会研究你即将用到的通用的技术环境。我们会提到许多信息,不要着急,你不需要马上记住所有内容,抓住它们的大概意思即可。
1.事务
PostgreSQL里面默认的事务隔离级别叫做Read Committed。这意味着如果多个事务尝试修改相同的数据,它们必须等待其他事务完成,之后才可以对结果数据进行操作。它们在一个先进先出的队列中等待。数据的最终结果是大多数人所能预料到的,反映的是最后的一个时序性的更改。
PostgreSQL并没有提供任何会导致错误读取的方法。错读是在其他人的事务期间查看数据的能力,并且假设它已经被执行完毕,从而使用了它。由于多版本并发控制产生了作用,所以PostgreSQL并不支持这种能力。
这里有一些其他可用于事务隔离的方法,你可以在页面http://www.postgresql.org/docs/9.2/static/transaction-iso.html中进行深入阅读。
我们需要特别注意的是,当非事务性的代码块(BEGIN..END)被定义的时候,PostgreSQL会像一个私人事务一样对待每一个独立的语句,并且在语句完成的时候立即执行它们。这样的操作就可以让其他事务有机会插入到你的语句中。一些程序设计语言在你的语句块周围会提供一个事务代码块,当然也并不是所有的语言都会提供。请查看你的语言文档,求证一下你是否在一个事务会话中运行程序。
当我们使用这两种主要的客户端与PostgreSQL进行交互时,事务行为是不同的。psql命令行客户端并没有为你提供事务块。你需要自己决定什么时候启动/停止一个事务。而pgAdmin3查询窗口将你提交的所有语句封装到了一个事务块中。这就是它提供的一个“取消”选项。如果事务被中止了,一个“回滚”操作将被执行,然后数据将回到它的前一个状态。
一些操作是不被认定为事务的。比如即使事务失败了且已经被回滚,但是一个“序列”对象将会继续执行。“CREATE INDEX CONCURRENTLY”需要它自己的事务管理,并且不应该在事务块内部被调用。VACUUM和CLUSTER也是同样的原理。
2.通用的错误报告和错误处理
如果你想在你的执行期间把状态提供给用户,你应该对这些命令比较熟悉:RAISE、NOTICE和NOTIFY。从事务性的角度看,它们之间的区别是即使它们被打包在一个事务中,RAISE和NOTICE会立即发送信息,然而NOTIFY需要等事务被处置之后才会发送一条消息。因此如果事务失败或回滚了,NOTIFY则不会立即向你通知任何消息。
3.用户定义函数(UDF)
编写用户定义函数是PostgreSQL的强大功能之一。函数可以使用许多不同的程序设计语言来编写,也可以使用这个语言所提供的任何控制结构,并且即使是采用“不受信”的语言,函数也可以执行PostgreSQL中可用的任何操作。
函数可以提供一些甚至非SQL直接相关的功能。接下来我们引用的一些示例将会展示如何获取网络地址信息、查询当前系统、移动文件,以及任何你心中所期望的事情。
那么,我们该如何利用PostgreSQL的这个优点呢?我们从声明一个函数开始:
image

但是,如果我们想把三个整数加在一起,该如何操作呢?
image

我们在前面提到过一个概念叫做函数重载。这个功能允许我们声明一个同名函数,但是使用的是不同的参数,如此可能会产生不同的行为。这个区别的巧妙之处在于它仅仅改变了函数中一个参数的数据类型。PostgreSQL开发的函数取决于函数参数与期望的返回类型的匹配程度。
但是,假设我们的打算是把任意数量的数字加起来,那该如何完成呢? PostgreSQL也有方法来完成。
image

这个函数允许我们传入任意数量的整数,并且返回一个正确的结果。这些函数当然不会处理real或者numeric类型的数据。为了处理其他的数据类型,借助这些类型,我们仅仅需要再次声明这个函数,并且使用相应的参数来调用它。
为了获取更多关于变量参数的信息,你可以查看http://www.postgresql.org/docs/9.2/static/xfunc-sql.html#XFUNC-SQL-VARIADIC-FUNCTIONS
4.其他参数
目前,将数据传入函数与从函数输出有多种方法。我们也可以声明IN/OUT参数、返回表,返回记录集合,也可以使用游标进行输入与输出。
这里有一个特殊的数据类型叫做ANY。这种类型允许不限定参数类型,同时也允许任何基础数据类型被传递到函数,然后由函数决定如何处理这个数据。
5.更多控制
一旦你按照需求编写了你的函数,PostgreSQL便会在函数执行上给你提供额外的控制。你可以控制这个函数能访问什么数据,也可以控制PostgreSQL如何解释执行函数的开销。
这里有两个声明可以为你的函数提供安全环境。第一个是Security Invoker,这是默认的安全环境。在默认环境里,调用者的权限通过函数来限制。
另一个环境是Security Definer。在这个环境下,函数创建者的用户权限是在函数执行期间生效的。一般情况下,为了特殊目标,这种方法可临时被用于提高用户的权限。
同时,PostgreSQL也可以定义函数的开销。这个可以帮助查询规划器评估调用这个函数会产生多大的消耗。更高次序的开销会迫使查询规划器修改这个访问路径,以降低函数被调用的频率。PostgreSQL文档将这些数字显示为一个cpu_operator_cost因子。这里有一些误导。这些数字和CPU运行周期并没有直接关系。它们仅仅和同其他函数进行结果比较时是相关的,这更像是一些国家的货币与欧盟其他国家的货币相比。一些国家的欧元比其他的更为有优势。
为了估计自己所定义的函数的复杂性,让我们从你所使用的语言开始。对于C,默认值是1 * number of records returned。对于Python,默认值是1.5。对于脚本语言,如PHP,更合适的默认值可能是100。对于plsh,你可能要使用150或更多,这取决于所涉及的外部工具的数量。而对于PL / pgSQL,默认值是100,这样运作起来似乎效率挺不错的。

相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
相关文章
|
8天前
|
SQL 关系型数据库 MySQL
【MySQL】:探秘主流关系型数据库管理系统及SQL语言
【MySQL】:探秘主流关系型数据库管理系统及SQL语言
15 0
|
21天前
|
SQL 安全 关系型数据库
关系型数据库控制语言(DCL)
DCL是SQL的一部分,用于控制数据库安全和访问权限。主要包括GRANT(授权)、REVOKE(撤销)和角色管理。GRANT用于给予用户访问数据库对象的权限,REVOKE用于撤销权限。角色管理则允许批量授予一组权限。DCL帮助管理员精细控制数据访问,确保安全性和完整性。不同RDBMS对DCL的实现可能略有差异,使用时需参考相应文档。
16 6
|
SQL 关系型数据库 数据库
PostgreSQL 12 文档: 部分 V. 服务器编程
部分 V. 服务器编程 这部分关于使用用户定义的函数、数据类型、触发器等扩展服务器功能。这些是高级主题,读者应该在理解了有关PostgreSQL的所有其他用户文档之后才阅读这些主题。这一部分的后面一些章节描述PostgreSQL发布中可用的服务器端编程语言,以及与服务器端编程语言相关的一般性问题。在深入研究服务器端编程语言的材料之前,请至少阅读第 37 章中前几节(覆盖函数)。
65 0
|
SQL XML 存储
PostgreSQL 12 文档: 部分 II. SQL 语言
部分 II. SQL 语言 这部份描述在PostgreSQL中SQL语言的使用。我们从描述SQL的一般语法开始,然后解释如何创建保存数据的结构、如何填充数据库以及如何查询它。中间的部分列出了在SQL命令中可用的数据类型和函数。剩余的部分则留给对于调优数据性能的重要方面。 这部份的信息被组织成让一个新用户可以从头到尾跟随它来全面理解主题,而不需要多次参考后面的内容。这些章都是自包含的,这样高级用户可以根据他们的选择阅读单独的章。这一部分的信息被以一种叙事的风格展现。需要查看一个特定命令的完整描述的读者应该去看看第 VI 部分。
76 0
|
关系型数据库 C语言 PostgreSQL
|
SQL 关系型数据库 数据库
PostgreSQL 10.1 手册_部分 II. SQL 语言
部分 II. SQL 语言 这部份描述在PostgreSQL中SQL语言的使用。我们从描述SQL的一般语法开始,然后解释如何创建保存数据的结构、如何填充数据库以及如何查询它。中间的部分列出了在SQL命令中可用的数据类型和函数。
1166 0
|
SQL 关系型数据库 PostgreSQL
PostgreSQL 10.1 手册_部分 II. SQL 语言_第 15 章 并行查询
第 15 章 并行查询 目录 15.1. 并行查询如何工作 15.2. 何时会用到并行查询? 15.3. 并行计划 15.3.1. 并行扫描 15.3.2. 并行连接 15.3.3. 并行聚合 15.3.4. 并行计划小贴士 15.4. 并行安全性 15.4.1. 为函数和聚合加并行标签 PostgreSQL能设计出利用多 CPU 让查询更快的查询计划。
1217 0
|
SQL 安全 关系型数据库
PostgreSQL 10.1 手册_部分 II. SQL 语言_第 15章 并行查询_15.4. 并行安全性
15.4. 并行安全性 15.4.1. 为函数和聚合加并行标签 规划器把查询中涉及的操作分类成并行安全、并行受限 或者并行不安全。并行安全的操作不会与并行查询的使用产生冲突。 并行受限的操作不能在并行工作者中执行,但是能够在并行查询的领导者中执行。
1140 0
|
SQL 关系型数据库 PostgreSQL
PostgreSQL 10.1 手册_部分 II. SQL 语言_第 15章 并行查询_15.3. 并行计划
15.3. 并行计划 15.3.1. 并行扫描 15.3.2. 并行连接 15.3.3. 并行聚合 15.3.4. 并行计划小贴士 因为每个工作者只执行完成计划的并行部分,所以不可能简单地产生一个普通查询计划并使用多个工作者运行它。
1284 0
|
SQL 关系型数据库 PostgreSQL
PostgreSQL 10.1 手册_部分 II. SQL 语言_第 15章 并行查询_15.2. 何时会用到并行查询?
15.2. 何时会用到并行查询? 有几种设置会导致查询规划器在任何情况下都不生成并行查询计划。为了让并行查询计划能够被生成,必须配置好下列设置。 max_parallel_workers_per_gather必须被设置为大于零的值。
1257 0