Dom*_*mra 12 sql postgresql transactions
我有一个项目和工作表:
项目
工作
项目以IN_PROGRESS开头,但是对它们执行工作,并交给工作人员进行更新.我有一个更新程序进程,当它们进入时会更新项目,并具有新状态.到目前为止我一直在做的方法是(伪代码):
def work(item: Item) = {
insideTransaction {
updateItemWithNewStatus(item)
jobs, items = getParentJobAndAllItems(item)
newJobStatus = computeParentJobStatus(jobs, items)
// do some stuff depending on newJobStatus
}
}
Run Code Online (Sandbox Code Playgroud)
那有意义吗?我希望这在并发环境中工作.我现在面临的问题是,当我只想在COMPLETE上执行一次逻辑时,COMPLETE会多次到达作业.
如果我将事务级别更改为SERIALIZABLE,我会收到"错误:由于事务之间的读/写依赖性而无法序列化访问"错误,如上所述.
所以我的问题是:
编辑:我重新打开了这个问题,因为我对以前的答案解释不满意.有人能为我解释一下吗?具体来说,我想要一些针对该伪代码的示例查询.
您可以在一个事务中使用SELECT FOR UPDATEon items和jobson处理两个表中受影响的行。这样就足以在没有SERIALIZABLE表锁或表锁开销的情况下增强整个操作的完整性。
我建议您创建一个在items表上进行插入或更新后调用的函数,并传递该项的PK:
CREATE FUNCTION process_item(item integer) RETURNS void AS $$
DECLARE
item items%ROWTYPE;
job jobs%ROWTYPE;
BEGIN -- Implicitly starting a transaction
SELECT * INTO job FROM jobs
WHERE id = (SELECT job_id FROM items WHERE id = item)
FOR UPDATE; -- Lock the row for other users
FOR item IN SELECT * FROM items FOR UPDATE LOOP -- Rows locked
-- Work on items individually
UPDATE items
SET status = 'COMPLETED'
WHERE id = item.id;
END LOOP;
-- Do any work on the job itself
END; -- Implicitly close the transaction, releasing the locks
$$ LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)
如果该作业或其任何关联项目上已经有其他进程正在执行,则该执行将暂停,直到释放该另一个锁为止。与此不同的是SERIALIZABLE,它将一直工作到失败为止,然后您必须在第二次尝试中重新进行所有处理。
如果您希望作业能够同时运行,则两者都不能SERIALIZABLE直接SELECT FOR UPDATE工作。
如果您使用 锁定该行SELECT FOR UPDATE,则另一个进程在执行 时将简单地阻塞,SELECT FOR UPDATE直到第一个进程提交事务。
如果这样做SERIALIZABLE,两个进程可以同时运行(处理同一行),但至少有一个进程在执行 a 时会失败,COMMIT因为数据库会检测到冲突。SERIALIZABLE如果它与数据库中同时进行的影响相关行的任何其他查询发生冲突,也可能会失败。使用的真正原因SERIALIZABLE正是如果您试图防止其他作业进行并发数据库更新,而不是阻止同一作业执行两次。
请注意,有一些技巧可以SELECT FOR UPDATE跳过锁定的行。如果你这样做,那么你就可以拥有实际的并发性。请参阅在 Postgresql 中选择未锁定的行。
我经常看到的另一种方法是更改“状态”列以具有第三个临时状态,该状态在处理作业时使用。通常会有“PENDING”、“IN_PROGRESS”、“COMPLETE”等状态。当您的流程搜索要执行的工作时,它会找到“待处理”作业,立即将其移至“IN_PROGRESS”并提交事务,然后继续工作,最后将其移至“完成”。缺点是如果进程在处理作业时终止,它将无限期地处于“IN_PROGRESS”状态。
| 归档时间: |
|
| 查看次数: |
465 次 |
| 最近记录: |