希望这是一个简单的问题,但我还没有找到一个好的答案。 我被可靠地告知,在PostgreSQL(特别是版本9.0.4)中,存储过程(用户定义的DB函数)本质上是事务性的,因为它们通过一个SELECT语句被调用,而SELECT本身就是一个事务。那么如何选择存储过程的隔离级别呢? 我相信在其他DBMS中,所需的事务块将被包装在START TRANSACTION块中,其中所需的隔离级别是一个可选参数。
以一个具体的虚构示例为例,假设我想要做到这一点:
CREATE FUNCTION add_new_row(rowtext TEXT)
RETURNS VOID AS
$$
BEGIN
INSERT INTO data_table VALUES (rowtext);
UPDATE row_counts_table SET count=count+1;
END;
$$
LANGUAGE plpgsql
SECURITY DEFINER;
假设我想确保此函数始终作为可序列化事务执行(是的,是的,PostgreSQL SERIALIZABLE 不是真正的可序列化,但这不是重点)。我不想要求以特定方式调用该函数。
START TRANSACTION ISOLATION LEVEL SERIALIZABLE;
SELECT add_new_row('foo');
COMMIT;
那么我如何将所需的隔离级别下推到函数中呢?我认为我不能仅在 BEGIN
语句中放置隔离级别,因为 手册中说:
重要的是不要混淆使用 BEGIN/END 来分组语句 在 PL/pgSQL 中与同名的 SQL 命令用于事务控制。 PL/pgSQL 的 BEGIN/END 仅用于 分组;它们不会开始或结束一个 事务。函数和触发器 程序总是在外部建立的事务中执行 查询——他们不能启动或提交 该事务,因为没有上下文可以 执行。
对我来说最明显的方法是在函数定义中的某个位置使用 SET TRANSACTION
,例如:
CREATE FUNCTION add_new_row(rowtext TEXT)
RETURNS VOID AS
$$
BEGIN
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
INSERT INTO data_table VALUES (rowtext);
UPDATE row_counts_table SET count=count+1;
END;
$$
LANGUAGE plpgsql
SECURITY DEFINER;
尽管这样做是可以被接受的,但我不能确定能够依赖它正常工作。 SET TRANSACTION 的文档说:
如果在没有先执行 START TRANSACTION 或 BEGIN 的情况下执行 SET TRANSACTION,则它似乎没有任何效果,因为事务将立即结束。
这让我感到困惑,因为如果我调用一个孤立的 SELECT add_new_row('foo');
语句,并且我没有禁用自动提交,那么我会期望该 SELECT 作为具有会话默认隔离级别的单行事务运行。
手册 还说:
当事务的第一个查询或数据修改语句(SELECT、INSERT、DELETE、UPDATE、FETCH 或 COPY)已经执行后,就不能再更改事务的隔离级别。
那么,如果从具有较低隔离级别的事务中调用该函数会发生什么,例如:
START TRANSACTION ISOLATION LEVEL READ COMMITTED;
UPDATE row_counts_table SET count=0;
SELECT add_new_row('foo');
COMMIT;
额外的问题:函数的语言是否有任何区别?在 PL/pgSQL 中与纯 SQL 中设置隔离级别会有所不同吗?
我是一个标准和文档化最佳实践的粉丝,因此任何可靠的参考资料都将不胜感激。
SET TRANSACTION
时会发生什么? - user330315SET TRANSACTION
是我需要的,而且其中包含的函数是被接受的,但有时这并不意味着太多(有些选项有时会被忽略),因此我正在寻找一种有文档支持的方法,而不仅仅是看起来有效的东西。 - beldazSET TRANSACTION
编译没有错误并且运行没有错误,它就不会被“吞掉” - 除非你启用了自动提交。但是那样的话,你根本就没有事务。 - user330315