问题
I'm writing an application in Node.js using PostgreSQL as database. But I have some problems. I have a table with information about resources in region:
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)
)
Users clicks on button, application calculates various parameters based on current_resource and then do current_resource - $calc_value. Because it's maybe very concurently I use transactions. But in process calculation maybe some errors, and we need to repeat calculations. Now I'm using SELECT ... FOR UPDATE for locking row with using current_resource. How i can do it with no lock using optimistic locking, if current value of current_resource is very important, and user who clicks first should use max. avalaible current_resource. In other words, I should implement acess queue for current_resource.
回答1:
For optimistic locking you need to define some means to check if a row has changed since you saw it last time. For example, lets just add another identifier:
alter table regions_indexes add version_id integer default 1 not null;
Now the application reads some row, shows the data to the user and waits until button is clicked. We must remember the value of version_id
we got.
After button is clicked, you perform all the necessary calculations. When you're ready to update the row, you lock the row and check whether version_id
has not changed. If it has not, increment version_id
and commit. If it has, bad luck --- you need to tell the user to repeat the operation because someone outrun him.
It may looks like this (in pseudocode):
-- 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;
But optimistic locking does not work well in situation of high concurrency. When conflicts are not rare, you will have to restart transactions frequently.
来源:https://stackoverflow.com/questions/34834357/optimistic-locking-queue