我有几个工作者,每个工作者都拥有自己与PostgreSQL的连接.工人们操纵着不同的桌子.
工作人员处理来自系统外部的并行请求.正在访问的表之一是用户表.当一些信息出现时,我首先需要确保表中的用户有一条记录.如果没有记录,我希望首先创建一个.
我正在使用以下成语:
if [user does not exist] then [create user]
代码[user does not exist]
是:
SELECT id FROM myschema.users WHERE userId='xyz'
我测试是否返回任何行.
(简化)代码[create user]
是:
INSERT INTO myschema.users VALUES ('xyz')
当我的系统处理有关同一用户的不同信息的并行流时,我经常会收到PostgreSQL错误:
Key (id)=(xyz) already exists
之所以发生这种情况是因为该SELECT
命令不返回任何行,然后另一个工作程序创建用户,任何我的工作程序都尝试执行相同操作,从而导致示例性并发错
根据PostgreSQL文档,默认情况下,每当我隐式启动一个事务时,只要我不提交它,表就会被锁定.我没有使用自动提交,我只在块中提交事务,例如在整个if-else
块之后.
实际上,我可以if-else
直接将这些东西放入SQL中,但它并不能解决我的锁定问题.我假设"赢家全部使用"范例将起作用,并且设法执行SELECT
命令的第一个工作人员将拥有锁,直到它调用COMMIT
.
我在SO上已经阅读了很多不同的主题,但我仍然不确定什么是正确的解决方案.我应该使用显式锁定表,因为隐式锁定不起作用?我怎样才能确保只有一个工人拥有一张桌子?
您必须关心事务隔离级别.它应该设置为" SERIALIZABLE
".
原因是Phantom Reads
- 事务不会锁定整个表,而只锁定事务已读取的行.
因此,如果另一个事务插入新数据,它们尚未锁定,则会出现错误.
Serializable通过阻止所有其他事务来避免这种情况,直到完成此事务.
你可以通过这样做
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
文件:http://www.postgresql.org/docs/9.1/static/transaction-iso.html
如果您想了解有关此主题的更多信息,我强烈建议您观看此视频:http://www.youtube.com/watch?v = zzz-Xbqp0g0A
实际上,经过ISOLATION LEVEL SERIALIZABLE
@maja提出的一些混乱之后,我发现了更简单的机制:
PERFORM pg_advisory_lock(id); ... # do something that others must wait for ... PERFORM pg_advisory_unlock(id);
这里id
是一个BIGINT
我可以根据我的应用程序的逻辑任意选择值.
这给了我正在寻找的力量和灵活性.