希望这是一个简单的问题,但我还没有找到一个体面的答案。我可靠地告知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
语句中,如the manual says
重要的是不要混淆使用 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
的{{3}}表示
如果没有执行SET TRANSACTION 它是一个先前的START TRANSACTION或BEGIN 似乎没有任何效果,因为 交易将立即结束。
这让我感到困惑,因为如果我调用一个单独的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不同?
我很喜欢标准并记录最佳实践,所以任何体面的参考都会受到赞赏。
答案 0 :(得分:17)
你做不到。
您可以做的是让您的函数检查当前事务隔离级别是什么,如果不是您想要的那个,则中止。您可以通过运行SELECT current_setting('transaction_isolation')
然后检查结果来执行此操作。
答案 1 :(得分:1)
该功能的语言没有任何区别。
这失败了:
test=# create function test() returns int as $$
set transaction isolation level serializable;
select 1;
$$ language sql;
CREATE FUNCTION
test=# select test();
ERROR: SET TRANSACTION ISOLATION LEVEL must be called before any query
CONTEXT: SQL function "test" statement 1
请注意,在您的特定示例中,您可以使用第一个表上的触发器执行此操作。只需确保完成行计数更新in a consistent order以避免死锁,并且您可以在可重复读取模式下正常工作。
我是标准的粉丝
PL /语言是特定于平台的。
答案 2 :(得分:0)
在PG中,您的程序不是单独的交易。也就是说,存储过程参与现有事务。
BEGIN TRAN
SELECT 1;
SELECT my_proc(99);
ROLLBACK TRAN;
据说,你必须设置事务开始的事务级别,这是在存储过程之外的。
一种选择是将服务器配置为在您最常使用的隔离区中运行,并为不同于服务器设置的边缘情况执行SET。
答案 3 :(得分:-1)
交易隔离是指您可以访问的其他可信交易中所做的更改。
如果要序列化执行,则必须使用锁。
您可以使用行后触发和更新计数。 “UPDATE row_counts_table”将锁定表,并且所有事务都将被序列化。 很慢。
在您的示例中,您有两个陈述。执行插入但更新必须等待其他事务,并且计数在此期间无效。