管理数据库并发的最佳方法是什么?

时间:2018-03-22 15:06:33

标签: sql database database-concurrency

我在处理并发时遇到了问题。

在下面的示例中,两个用户A和B编辑同一发票并对其进行不同的更改。如果他们两个同时点击保存我希望其中一个成功,另一个失败。否则,生成的发票将是不合需要的"合并发票"。

以下是在PostgreSQL中测试的示例(但我认为这个问题应该与数据库无关):

create table invoice (
  id int primary key not null,
  created date
);

create table invoice_line (
  invoice_id int, 
  line numeric(6), 
  amount numeric(10,2),
  constraint fk_invoice foreign key (invoice_id) references invoice(id)
  );

insert into invoice(id, created) values (123, '2018-03-17');
insert into invoice_line (invoice_id, line, amount) values (123, 1, 24);
insert into invoice_line (invoice_id, line, amount) values (123, 2, 26);

因此发票的初始行是:

invoice_id  line  amount
----------  ----  ------
       123     1      24
       123     2      26

现在,用户A编辑发票,删除第2行并点击保存

-- transaction begins

set transaction isolation level serializable;

select * from invoice where id = 123; -- #1 will it block the other thread?

delete invoice_line where invoice_id = 123 and line = 2;

commit; -- User A would expect the invoice to only include line 1.

同时,用户B编辑发票,添加第3行,然后点击保存

-- transaction begins

set transaction isolation level serializable;

select * from invoice where id = 123; -- #2 will this wait the other thread?

insert into invoice_line (invoice_id, line, amount) values (123, 3, 45);

commit; -- User B would expect the invoice to include lines 1, 2, and 3.

不幸的是,两个事务都成功了,我得到了合并的行(损坏的状态):

invoice_id  line  amount
----------  ----  ------
       123     1      24
       123     3      45

由于这不是我想要的,我有什么选择来控制并发?

2 个答案:

答案 0 :(得分:1)

这不是数据库并发问题。数据库的ACID属性是关于完成事务,同时保持数据库完整性。在您描述的情况下,事务是正确的,数据库正在正确处理它们。

你想要的是一种锁定机制,本质上是一种信号量,它保证一次只有一个用户可以拥有对数据的写访问权。您可能可以依赖数据库锁定机制,捕获锁定未能发生的时间。

但是,我建议采用另外两种方法之一。如果您对只在应用程序逻辑中进行的更改感到满意,那么请将锁定机制放在那里。有一个用户可以“锁定”表或记录的地方;然后别让别人碰它。

你可以更进一步。您可以要求用户获取表的“所有权”以进行更改。然后,除非用户是进行更改的用户,否则您可以实现失败的触发器。

而且,您可能会想到其他解决方案。我真正想要指出的是,你的用例不在RDBMS默认的范围之内(因为它们会让两个事务成功完成)。因此,您需要为任何数据库(我熟悉的)提供额外的逻辑。

答案 1 :(得分:0)

作为一般规则,发票行项目在发布后不应编辑或删除。如果客户需要反转费用,那么典型的方法是添加一个新的交易,该交易可以记入金额,可能还有一个交叉引用字段,其中包含正在被撤销的订单项的ID。这种方法的优点是:(1)您可以修改客户的余额而无需返回并重新预订任何先前的语句周期,(2)您不会遇到像这样难以解决的并发问题。< / p>

如果发票尚未过帐,您仍然不允许修改订单项。相反,您将取消之前的发票并使用所有新订单项创建一个新发票。这再次避免了手头的并发问题。