use*_*648 3 postgresql concurrency atomic plpgsql
我认为如果一个进程从唯一的用户ID中选择余额并尝试执行插入,则会错误地更新余额,但是另一个进程会在此之前读取余额.我该如何解决?
CREATE OR REPLACE FUNCTION incBalance(INTEGER, BIGINT) RETURNS void AS $$
DECLARE
balanceRecord record;
newBalance bigint;
BEGIN
FOR balanceRecord IN
SELECT balance FROM users WHERE userid = $1
LOOP
newBalance := balanceRecord.balance + $2;
UPDATE users SET balance = newBalance WHERE userid = $1;
END LOOP;
RETURN;
END;
$$ LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)
对于此特定查询,您可以将其重写为单个SQL语句:
UPDATE users SET balance = balance + $2 WHERE userid = $1;
Run Code Online (Sandbox Code Playgroud)
更一般地说,您希望让事务系统处理原子性和数据一致性.在Postgres中,存储过程总是在事务上下文中执行 - 如果您没有从显式事务块中调用它,它将为您创建一个.
http://www.postgresql.org/docs/9.2/static/sql-set-transaction.html讨论了如果默认值不够严格,如何设置隔离级别.
您将需要阅读http://www.postgresql.org/docs/9.2/static/mvcc.html以帮助确定哪个级别适合特定存储过程.请注意第13.2.2节和第13.2.3节,它们警告更高的隔离级别受到应捕获的序列化异常的影响,并且重试事务作为确保一致性的机制.
如果我有这样的过程,我在过程的第一个BEGIN块的开头添加一个语句,以确保事务以足够的隔离级别运行.如果没有工作已在交易已经完成的是,它会在必要时提出.如果调用上下文是已完成工作的事务,则如果封闭事务块尚未充分提高隔离级别,则会导致错误.如果隔离级别已高于此处指定的隔离级别,则不会降低隔离级别.
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3979 次 |
| 最近记录: |