热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

[译稿]PostgreSQL中匿名事务的使用

2019独角兽企业重金招聘Python工程师标准原文链接自主事务是从一个主事务启动的另一个事务,用于执行独立于调用事务提交或取消的SQL命令。最经典的用例是将用

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

原文链接

自主事务是从一个主事务启动的另一个事务,用于执行独立于调用事务提交或取消的SQL命令。

最经典的用例是将用户在数据库上完成的所有操作插入日志记录表,无论事务成功还是失败。使用PostgreSQL,当事务中的某些事务失败时,事务中完成的所有更改都将被取消。出于同样的原因,当自治事务失败时,没有异常会被转发到调用事务,它可以成功执行结束。

译者注: 类似finally, 但是独立的。

try {
}catch {
}
finally {
}

我们可以将自治事务视为一个独立的工作单元,就像在另一个会话中执行SQL语句一样。在这种情况下,在主事务中提交的操作在自主事务中不可见,直到主事务被终止和提交。

在自主事务中执行的SQL操作可以在主事务提交时显示,并且主事务中的隔离级别不是SERIALIZABLE或REPEATABLE READ。

下面是一个非常简单的Oracle函数示例,它使用自主事务独立地记录数据库中执行的所有操作,而不管事务的最终结果是什么。

目前没有办法在PostgreSQL 原生版本中存在(截止2016-08-19, 后面有人提交了补丁)。单个事务中的每个更改都将被取消或随事务一起提交。然而,很长时间以来,有一些解决方案来执行这样的事务。它变得更容易,在PostgreSQL 9.5版本中,旧的解决方案是使用dblink扩展名与后端的专用连接。最新的解决方案是使用pg_background扩展来实现自主事务,内核实现。我在下面的章节中描述两种方式。

“旧的方式”:独立连接:

要使用PostgreSQL获得相同的自治事务行为,您只需打开一个到PostgreSQL的新连接,并在此新会话中执行SQL语句。

为了这个工作,我们通常使用dblink或PL/Proxy contrib模块很长时间。它们都允许创建到PostgreSQL服务器的新连接,并在这个新连接内使用自己的事务执行SQL语句。

例如,Ora2Pg(截止版本17.4)使用带有使用dblink的包装器的pragma AUTONOMOUS_TRANSACTION转换所有Oracle函数或过程。此包装器接受函数的名称并添加一个_atx后缀。

使用上面的Oracle示例,Ora2Pg将首先转换此函数并使用_atx后缀重命名。

CREATE OR REPLACE FUNCTION log_action_atx (username text, event_date timestamp, msg text
) RETURNS VOID AS
$body$
BEGININSERT INTO table_tracking VALUES (nextval('log_seq'), username, event_date, msg);
END;
$body$
LANGUAGE PLPGSQL

然后,它使用将由应用程序或另一个存储过程调用的dblink创建的包装函数。

--
-- dblink wrapper to call function log_action as an autonomous transaction
--
CREATE OR REPLACE FUNCTION log_action (username text, event_date timestamp, msg text
) RETURNS VOID AS
$body$
DECLARE-- Change this to reflect the dblink connection stringv_conn_str text := 'port=5432 dbname=testdb host=localhost user=pguser password=pgpass';v_query text;BEGINv_query := 'SELECT true FROM log_action_atx ( ' || quote_nullable(username) ||',' || quote_nullable(event_date) || ',' || quote_nullable(msg) || ' )';PERFORM * FROM dblink(v_conn_str, v_query) AS p (ret boolean);END;
$body$
LANGUAGE plpgsql SECURITY DEFINER;

此方法工作得很好,但需要一些手动编辑来设置dblink连接参数。 还有一些安全问题,连接密码在功能代码中。 性能也不是最优的。

“新的使用方式”:使用动态后台workder 进程:

希望自从添加动态后台工作人员和Robert Haas的大部分工作以来,可能是因为PostgreSQL v9.5使用pg_background扩展创建自主事务。 此扩展提供了一些其他优势,但本文只是解释如何以自主方式调用函数或SQL语句。

如果我们回到前面的例子,这里是Ora2Pg如何导出函数使用pg_background扩展在即将到来的17.5版本。

--
-- pg_background wrapper to call function log_action as an autonomous transaction
--
CREATE OR REPLACE FUNCTION log_action (username text, event_date timestamp, msg text
) RETURNS VOID AS
$body$
DECLAREv_query text;BEGINv_query := 'SELECT true FROM log_action_atx ( ' || quote_nullable(username) ||',' || quote_nullable(event_date) || ',' || quote_nullable(msg) || ' )';PERFORM * FROM pg_background_result(pg_background_launch(v_query)) AS p (ret boolean);
END;
$body$
LANGUAGE plpgsql SECURITY DEFINER;CREATE OR REPLACE FUNCTION log_action_atx (username text, event_date timestamp, msg text
) RETURNS VOID AS
$body$
BEGININSERT INTO table_tracking VALUES (nextval('log_seq'), username, event_date, msg);
END;
$body$
LANGUAGE PLPGSQL
;

在这里,我们谈到了自动PL/SQL的Oracle代码转换,但是更简单的方法显然是直接调用函数:

CREATE OR REPLACE FUNCTION log_action (username text, event_date timestamp, msg text
) RETURNS text AS
$body$
DECLAREs_id integer;
BEGININSERT INTO table_tracking VALUES (nextval('log_seq'), username, event_date, msg) RETURNING id INTO s_id;RETURN 'Message inserted into table_tracking with id: '|| s_id;
END;
$body$
LANGUAGE plpgsql;

如果我们想等待自治事务的结果并使用结果:

SELECT * FROM pg_background_result(pg_background_launch('SELECT log_action(...)')) AS p (ret text);

否则,对pg_background_launch() 进行简单调用并放在后台执行自主事务。 主事务将继续,而不等待在后台启动的自主事务的结果。

SELECT pg_background_launch('SELECT log_action(...)');

CREATE OR REPLACE FUNCTION test_autonomous_transaction (username text, msg text
) RETURNS text AS
$body$
DECLAREat_pid integer;at_result text;
BEGINSELECT INTO at_pid pg_background_launch('SELECT log_action('||username||','||now()||','||msg)');... do something ...SELECT INTO at_result * FROM pg_background_result(at_pid) as (result text);RETURN at_result;
END;

下面是一个完整的使用Ora2pg 创建的函数

CREATE TABLE table_tracking ( id integer, username text, event_date timestamp, msg text);CREATE SEQUENCE log_seq START 1;CREATE OR REPLACE FUNCTION log_action_atx (username text, event_date timestamp, msg text
) RETURNS VOID AS
$body$
BEGININSERT INTO table_tracking VALUES (nextval('log_seq'), username, event_date, msg);
END;
$body$
LANGUAGE PLPGSQL ;CREATE OR REPLACE FUNCTION log_action (username text, event_date timestamp, msg text
) RETURNS VOID AS
$body$
DECLAREv_query text;
BEGINv_query := 'SELECT true FROM log_action_atx ( ' || quote_nullable(username) || ',' || quote_nullable(event_date) || ',' || quote_nullable(msg) || ' )';PERFORM * FROM pg_background_result(pg_background_launch(v_query)) AS p (ret boolean);
END;
$body$
LANGUAGE plpgsql SECURITY DEFINER;

gilles=# TRUNCATE table_tracking ;
TRUNCATE TABLE
gilles=# ALTER SEQUENCE log_seq restart 1;
ALTER SEQUENCE
gilles=# BEGIN;
BEGIN
gilles=# SELECT * from table_tracking;id | username | event_date | msg
----+----------+------------+-----
(0 ligne)gilles=# SELECT log_action('gilles', 'now', 'Add autonomous_transaction article');log_action
------------(1 ligne)gilles=# SELECT * from table_tracking;id | username | event_date | msg
----+----------+----------------------------+------------------------------------1 | gilles | 2016-08-19 11:55:08.859347 | Add autonomous_transaction article
(1 ligne)gilles=# ROLLBACK;
ROLLBACK
gilles=# SELECT * from table_tracking;id | username | event_date | msg
----+----------+----------------------------+------------------------------------1 | gilles | 2016-08-19 11:55:08.859347 | Add autonomous_transaction article
(1 ligne)

In this last example, we can see that it is perfectly possible to work on something else in the main transaction while the autonomous transaction is running in the background:

在最后一个例子中,我们可以看到,当自主匿名事务在后台运行时,主事务中完全可以在处理其他事情:

gilles=# BEGIN;
BEGIN
gilles=# SELECT * FROM table_tracking;id | username | event_date | msg
----+----------+----------------------------+------------------------------------1 | gilles | 2016-08-19 14:00:12.573144 | Add autonomous_transaction article2 | gilles | 2016-08-19 14:01:20.83565 | Add autonomous_transaction article
(2 lignes)gilles=# SELECT pg_background_launch($$SELECT pg_sleep(30); SELECT log_action('gilles','now','Add autonomous_transaction article');$$);pg_background_launch
----------------------26170
(1 ligne)gilles=# SELECT * FROM table_tracking;id | username | event_date | msg
----+----------+----------------------------+------------------------------------1 | gilles | 2016-08-19 14:00:12.573144 | Add autonomous_transaction article2 | gilles | 2016-08-19 14:01:20.83565 | Add autonomous_transaction article
(2 lignes)... Attente de 30 secondes ...gilles=# SELECT * FROM table_tracking;id | username | event_date | msg
----+----------+----------------------------+------------------------------------1 | gilles | 2016-08-19 14:00:12.573144 | Add autonomous_transaction article2 | gilles | 2016-08-19 14:01:20.83565 | Add autonomous_transaction article3 | gilles | 2016-08-19 14:05:42.181332 | Add autonomous_transaction article
(3 lignes)gilles=# SELECT * FROM pg_background_result(26170) as p (result text);result
-------------------------------------------------Message inserted into table_tracking with id: 3
(1 ligne)gilles=# ROLLBACK;
ROLLBACK

这里,我们在自治事务中添加了等待30秒( ```SELECT pg_sleep(30);), 我们可以看到pg_background_launch() 函数立即返回。 如果我们在主事务中等待30秒,那么我们可以看到, 当自治事务结束时,我们在table_tracking表中有新的条目。 现在我们可以使用pg_background_launch()返回的pid和 pg_background_result()函数来获得自治事务的结果。

这种在后台执行自动事务的特性,可以让你绕过一些PostgreSQL的限制, 比如在事务中运行CREATE INDEX CONCURRENTLY语句。

警告: 需要超级用户来创建此扩展,一旦创建, 任何有权访问数据库的用户将被授予执行这些pg_background_...()函数。 即使对象的ACL被保留,强制要求真正小心数据库访问和定期审计数据库。 要防止所有用户执行这些功能, 最好的办法是将该扩展重定位到专有的模式(schema)下, 只有授权用户才能被授予使用权限。这可以使用以下命令完成:

CREATE SCHEMA bgw_schema;
ALTER EXTENSION pg_background SET SCHEMA bgw_schema;
GRANT USAGE ON SCHEMA bgw_schema TO ;

在由授权用户创建的函数中使用SECURITY DEFINER属性并使用 bgw_schema.pg_background_...()函数将有助于控制安全风险。


转:https://my.oschina.net/innovation/blog/782635



推荐阅读
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • Python正则表达式学习记录及常用方法
    本文记录了学习Python正则表达式的过程,介绍了re模块的常用方法re.search,并解释了rawstring的作用。正则表达式是一种方便检查字符串匹配模式的工具,通过本文的学习可以掌握Python中使用正则表达式的基本方法。 ... [详细]
  • 闭包一直是Java社区中争论不断的话题,很多语言都支持闭包这个语言特性,闭包定义了一个依赖于外部环境的自由变量的函数,这个函数能够访问外部环境的变量。本文以JavaScript的一个闭包为例,介绍了闭包的定义和特性。 ... [详细]
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • JavaSE笔试题-接口、抽象类、多态等问题解答
    本文解答了JavaSE笔试题中关于接口、抽象类、多态等问题。包括Math类的取整数方法、接口是否可继承、抽象类是否可实现接口、抽象类是否可继承具体类、抽象类中是否可以有静态main方法等问题。同时介绍了面向对象的特征,以及Java中实现多态的机制。 ... [详细]
  • JVM 学习总结(三)——对象存活判定算法的两种实现
    本文介绍了垃圾收集器在回收堆内存前确定对象存活的两种算法:引用计数算法和可达性分析算法。引用计数算法通过计数器判定对象是否存活,虽然简单高效,但无法解决循环引用的问题;可达性分析算法通过判断对象是否可达来确定存活对象,是主流的Java虚拟机内存管理算法。 ... [详细]
  • 本文介绍了RPC框架Thrift的安装环境变量配置与第一个实例,讲解了RPC的概念以及如何解决跨语言、c++客户端、web服务端、远程调用等需求。Thrift开发方便上手快,性能和稳定性也不错,适合初学者学习和使用。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
author-avatar
hupi12丨
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有