2

我们有一个名为 Contracts 的表。这些合同记录由外部站点的用户创建,必须由内部站点的工作人员批准或拒绝。当合同被拒绝时,它只是从数据库中删除。但是,当它被接受时,会生成一条名为 Contract Acceptance 的新记录,该记录将写入其自己的表中,并从合同中存在的数据派生而来。

问题是两名内部员工最终可能会各自打开同一份合同。第一个用户接受并生成合同接受记录。然后,在页面上仍然打开相同的合同记录的情况下,第二个用户再次接受合同,创建重复的接受记录。

解决这个问题的快速而肮脏的方法是在合同被接受之前从数据库中检索合同,检查状态,并产生一条错误消息,说明它已被接受。这可能适用于大多数情况,但用户仍然可以同时单击接受按钮并通过此验证码偷偷摸摸。

我还考虑过数据层深处的线程锁,它可以防止两个线程同时进入相同的代码区域,但是应用程序存在于两个负载平衡的服务器上,因此用户可以在不同的服务器上使这种方法无用。

我能想到的唯一方法必须存在于数据库中。从概念上讲,我想以某种方式锁定存储过程或表,使其不能同时更新两次,但也许我在这里对 Oracle 的了解不够。更新如何工作?更新请求是否以某种方式排队,以便它们不会同时发生?如果是这样,我可以检查 SQL 中记录的状态,并在 out 参数中返回一个值,说明它已被接受。但是如果更新请求没有排队,那么两个人仍然可以同时进入更新 sql。

寻找有关如何解决此问题的好建议。

4

2 回答 2

2

首先,如果每个合同只能有一个合同接受,那么合同接受应该将合同 ID 作为其自己的主(或唯一)键:这将使重复不可能。

其次,为了防止第二个用户在第一个用户接受合同时尝试接受合同,您可以让接受过程锁定合同行:

select ...
from Contract
where contract_id = :the_contract
for update nowait;

insert into Contract_Acceptance ...

第二个用户的接受尝试将失败并出现异常:

ORA-00054: resource busy and acquire with nowait specified
于 2011-08-11T13:36:01.777 回答
1

一般来说,有两种方法可以解决这个问题

选项 1:悲观锁定

在这种情况下,您很悲观,因此在选择该行时将其锁定在表中。当用户查询Contracts表时,他们会做类似的事情

SELECT *
  FROM contracts
 WHERE contract_id = <<some contract ID>>
   FOR UPDATE NOWAIT;

谁先选择记录,谁就锁定它。第二个选择记录的人将得到一个 ORA-00054 错误,然后应用程序将捕获该错误并让他们知道另一个用户已经锁定了该记录。当第一个用户完成他们的工作时,他们将他们的 INSERT 发布到Contract_Acceptance表中并提交他们的事务。这将释放对Contracts表中行的锁定。

选项 2:乐观锁定

在这种情况下,您对两个用户不会发生冲突持乐观态度,因此您最初不会锁定记录。相反,您可以选择所需的数据以及Last_Updated_Timestamp添加到表中的列(如果该列尚不存在)。就像是

SELECT <<list of columns>>, Last_Updated_Timestamp
  FROM Contracts
 WHERE contract_id = <<some contract ID>>

当用户接受合同时,在INSERT进入之前Contract_Acceptance,他们会发出一个UPDATEon Contracts

UPDATE Contracts
   SET last_updated_timestamp = systimestamp
 WHERE contract_id = <<some contract ID>>
   AND last_update_timestamp = <<timestamp from the initial SELECT>>;

第一个执行此更新的人将成功(该语句将更新 1 行)。第二个这样做的人将更新 0 行。应用程序检测到更新没有修改任何行的事实,并告诉第二个用户其他人已经处理了该行。

在任一情况下

无论哪种情况,您都可能希望向表中添加UNIQUE约束Contract_AcceptanceContract_Acceptance这将确保任何给定的表中只有一行Contract_ID

ALTER TABLE Contract_Acceptance
  ADD CONSTRAINT unique_contract_id UNIQUE (Contract_ID)

这是永远不需要的第二道防线,但可以在应用程序未正确实现其逻辑的情况下保护您。

于 2011-08-11T13:40:09.343 回答