在Postgres

时间:2017-07-22 23:07:34

标签: sql postgresql transactions

假设我们有一个表格,用于描述用户的一篮子水果的内容。这基本上只是用户的映射 - >水果清单。

我们定期查询远程数据源并获取特定用户的新列表。然后,我们尝试用一组新行完全替换所有用户的当前行。例如:

在:

user     fruit        freshness
-------------------------------
...
47       'apple'      0.1
47       'pear'       9.5
47       'pear'       2.8
...

后:

user     fruit        freshness
-------------------------------
...
47       'apple'      93.9034
47       'banana'     0
...

给定一组新的行,有没有办法在postgres中进行 atomic 的替换?

以下不起作用:

BEGIN
DELETE FROM basket WHERE user=47
INSERT INTO basket ...
COMMIT

如果我们同时进行多个交易,postgres会很乐意订购这样的命令(没有锁定问题):

BEGIN
BEGIN
DELETE
DELETE
INSERT
INSERT  <---- data inserted twice here
COMMIT
COMMIT

这将导致行数的两倍。

许多类似问题的答案声称第一个DELETE将锁定有问题的行,第二个事务必须等待,但这不是真的(无论出于何种原因)。这两笔交易都很高兴地在这里互相踩踏。

备注:

  • 优选地,解决方案会使读者无法看到中间状态(没有行),但此时我可以没有它。
  • 我的数据库结构错误了吗?我真的希望能够存储user - >的地图fruit[]。我可以把它全部放在一个jsonb列中,但是我不能在行上运行连接。加入很好:(

1 个答案:

答案 0 :(得分:1)

您可以锁定要修改的user_id。如果您有users表,则选择相应的行进行更新:

BEGIN;
SELECT user_id FROM users WHERE user_id = 47 FOR UPDATE;
DELETE FROM basket WHERE user_id = 47;
INSERT INTO basket ...
COMMIT;

或者,您可以使用咨询锁,例如:

BEGIN;
SELECT pg_advisory_lock('basket'::regclass::int, 47);
DELETE FROM basket WHERE user_id = 47;
INSERT INTO basket ...
SELECT pg_advisory_unlock('basket'::regclass::int, 47);
COMMIT;

阅读the documentation.

中的明确锁定