我有一个这样的表结构:
ParentObject(ObjectId 字符串,.....)
ChildObject(ObjectId 字符串、ParentId 字符串、Atribute1 字符串、状态整数...)
ParentObject 记录有多条(近 1000 条),每个 ParentObject 记录表都被 ChildObject 表中的多条记录(近 50 条)引用。
我有两个并行进程在不同的机器上运行,它们使用 OCI 库调用循环执行此查询。
UPDATE ChildObject SET Attribute1='<process_name>' WHERE ObjectId = ANY
(SELECT TOP 100 ObjectId FROM ChildObject alias1 WHERE State = 0
AND NOT EXISTS
(SELECT * FROM ChildObject alias2 WHERE alias2.State = 0 AND
alias2.Attribute1 <> ' ' AND alias2.ParentId = alias1.ParentId))
语法可能并不完美。逻辑是每个进程在一次运行中更新 100 个 ChildObject 记录,并将 Attribute1 设置为进程名称(如果尚未设置),休眠一段时间并再次开始更新。
我的要求是引用同一ParentObject记录的所有ChildObject记录应该由一个进程更新。例如,如果 Process1 更新了具有相同 ParentId 的 ChildObject 的 10 条记录,则具有相同 ParentId 的 ChildObject 的其余记录应由 Process1 更新,而不是由 Process2 更新。
由于进程是并行运行的,因此一些 ChildObject 记录在一个进程中更新,而一些具有相同 ParentId 的 ChildObject 记录在另一进程中更新。
Select..For Update 在我的情况下不起作用,因为更新发生在 ChildObject 表中的不同记录上。
锁定整个 ChildObject 表可能不是一个好的解决方案。
您能建议我如何实现这种同步吗?
谢谢,
瓦纳西
如何将 ProcessName 列添加到 ParentObject 表中。如果 ProcessName 为 null,想要更新子进程的进程首先必须在父进程上设置 ProcessName(在更新查询中包含条件,之前不要检查它)。即使两个进程尝试同时设置父 ProcessName,也只有一个进程会成功。然后,该进程读取父进程的 ProcessName,如果是它自己的名称,则继续更新子进程。
如果您无法添加或修改表,可以将查询更改为:
UPDATE ChildObject
SET Attribute1='<process_name>'
WHERE ObjectId = ANY (
SELECT TOP 100 ObjectId
FROM ChildObject alias1
WHERE State = 0 AND NOT EXISTS (
SELECT *
FROM ChildObject alias2
WHERE alias2.Attribute1 <> ' '
AND alias2.Attribute1 <> '<process_name>'
AND alias2.ParentId = alias1.ParentId
)
)
第一步只需锁定父行即可。这将阻止任何其他会话(具有相同的锁定机制)处理该行
SELECT *
FROM ParentObject
WHERE ObjectID = <<whatever>>
FOR UPDATE;
UPDATE ChildObject ...
根据 Oracle 版本以及您正在执行的操作,您可能不希望线程无限期地阻塞等待锁定
ParentObject
行。您可能想要执行 FOR UPDATE NOWAIT
,如果无法锁定父行则捕获异常,然后继续处理下一行。或者您可能想要执行 FOR UPDATE SKIP LOCKED
来确定下一个要处理的 ObjectID
。
我们遇到了类似的问题,多个 k8s pod 并行执行下面的更新查询并陷入死锁
因此,我们需要锁定会话 1 将要更新的行集,这将阻止其他会话更新相同的行。
SELECT * FROM
(select group_slice_id,slice_id FROM queue_master
WHERE batch_run_id = 'STP_1693389464930_2015-04-03_1696424282917_1' AND COMPONENT_ID = '1_5'
AND Control_plane_pod_id is null AND status is null AND pod_id is null) WHERE rownum <= 10
FOR UPDATE SKIP LOCKED;
update queue_master set status = 'CP-IN-PROGRESS',
START_TIME = CURRENT_TIMESTAMP, control_plane_pod_id = 'cp-pod-1', CP_POD_IP='192.11.11.0'
where batch_run_id = 'STP_1693389464930_2015-04-03_1696424282917_1' and status is null and Control_plane_pod_id is null
and COMPONENT_ID = '1_5' and
(group_slice_id,slice_id) in (select * from
(select group_slice_id,slice_id from queue_master
where batch_run_id = 'STP_1693389464930_2015-04-03_1696424282917_1' and COMPONENT_ID = '1_5'
and Control_plane_pod_id is null and status is null and pod_id is null) where rownum <= 10 );
带有 FOR UPDATE 的选择查询将锁定这些行集,并且只有在提交或回滚后才会释放锁定
并且 FOR UPDATE SKIP LOCKED 将锁定下一组行,而不是等待第一个会话释放锁