我有users
桌子
CREATE TABLE tasks(
id UUID PRIMARY KEY,
status TEXT REFERENCES statuses, -- available statuses are 'NEW', 'PENDING', 'PROCESSING', 'COMPLETE'
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NULL
);
我想确保在多次并发更新尝试的情况下,只有单个进程的状态会从 更改为PENDING
,PROCESSING
其余进程应该观察到零更新行。
任务状态变化是由处理外部 HTTP 请求驱动的,重要的是只有一个请求能够成功,其余的应该检测变化并返回 409 冲突,我们无法控制可以获得多少个并发请求。
更新语句是作为较大事务的一部分执行的第一件事,在update
事件进程观察到它更新了tasks
行(更新的行数为 1)之后,它会在其他表中插入/更新一些数据等。
我已经测试了以下更新查询
UPDATE tasks
SET status = 'PROCESSING'
WHERE id = '00000000-0000-0000-0000-000000000000' AND status = 'PENDING'
使用 Java JDBC、多线程、连接等,我观察到在使用默认隔离级别时只有单个进程进行更新READ COMMITTED
。
当我思考这个问题时,我认为我需要FOR UPDATE
语句UPDATE
或SERIALIZABLE
事务的隔离级别,但在实际测试中,我无法在多次更新的情况下捕获不一致的情况。
id
我的问题是:通过过滤并status
确保只有单个进程执行更新是否足够,或者我需要采取其他措施来排除一般的意外行为?
是的。
使用默认隔离级别
READ COMMITTED
,第一个更新行的事务会对其进行写锁定。在该事务提交之前,并发事务仍将看到status = 'PENDING'
给定的id
。但是该行是写锁定的,任何想要写入该行的事务都必须等到锁定被释放。当锁定事务提交或回滚时,就会发生这种情况。然后,等待的事务将依次重新评估过滤器并查看新状态status = 'PROCESSING'
- 并返回空值,这似乎正是您想要的。仅当等待事务在释放锁后可能发现行未发生改变(仍处于待处理状态)时,等待才对您的特定查询有意义,这种情况仅在以下情况下发生
ROLLBACK
- 或者如果不同的操作可能已对该行进行写锁定。如果您不想等待,请使用
SKIP LOCKED
或NOWAIT
。请参阅: