乐观锁队列

use*_*409 3 postgresql transactions

我正在使用 PostgreSQL 作为数据库在 Node.js 中编写一个应用程序。但我有一些问题。我有一个表,其中包含有关该地区资源的信息:

CREATE TABLE regions_indexes
(
  id integer NOT NULL,
  resource_type integer NOT NULL,
  current_resource integer,
  maximum_resource integer,
  CONSTRAINT regions_indexes_pkey PRIMARY KEY (id, resource_type)
)
Run Code Online (Sandbox Code Playgroud)

用户单击按钮,应用程序根据 current_resource 计算各种参数,然后执行 current_resource - $calc_value。因为我可能非常同时地使用事务。但在计算过程中可能会出现一些误差,需要重新计算。现在我使用 SELECT ... FOR UPDATE 来使用 current_resource 锁定行。如果 current_resource 的当前值非常重要,并且首先单击的用户应该使用 max,我如何使用乐观锁定在没有锁定的情况下做到这一点。可用的 current_resource。换句话说,我应该为 current_resource 实现访问队列。

Ego*_*gov 5

对于乐观锁定,您需要定义一些方法来检查自上次看到以来一行是否已更改。例如,让我们添加另一个标识符:

alter table regions_indexes add version_id integer default 1 not null;
Run Code Online (Sandbox Code Playgroud)

现在应用程序读取一些行,向用户显示数据并等待单击按钮。我们必须记住的价值version_id

单击按钮后,您将执行所有必要的计算。当您准备好更新该行时,您可以锁定该行并检查是否version_id尚未更改。如果没有,则增加version_id并提交。如果有,那么运气不好——你需要告诉用户重复该操作,因为有人跑赢了他。

它可能看起来像这样(伪代码):

-- remember version_id
select *
from regions_indexes
where id = ... and resource_type = ...;

-- wait for user click
-- you can wait for a long time, because no lock is yet acquired
...

update regions_indexes
set current_resource = current_resource - ..., version_id = version_id + 1
where id = ... and resource_type = ...
returning version_id;

if new_version_id = old_version_id + 1 then
  -- success, commit
else 
  -- fail, rollback
end if;
Run Code Online (Sandbox Code Playgroud)

但乐观锁在高并发的情况下效果不佳。当冲突并不少见时,您将不得不频繁地重新启动事务。