数据库 - 对不同记录同步执行更新查询

问题描述 投票:0回答:3

我有一个这样的表结构:

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 表可能不是一个好的解决方案。

您能建议我如何实现这种同步吗?

谢谢,

瓦纳西

database oracle synchronization sql-update
3个回答
0
投票

如何将 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
    )
)

0
投票

第一步只需锁定父行即可。这将阻止任何其他会话(具有相同的锁定机制)处理该行

SELECT *
  FROM ParentObject
 WHERE ObjectID = <<whatever>>
   FOR UPDATE;

UPDATE ChildObject ...

根据 Oracle 版本以及您正在执行的操作,您可能不希望线程无限期地阻塞等待锁定

ParentObject
行。您可能想要执行
FOR UPDATE NOWAIT
,如果无法锁定父行则捕获异常,然后继续处理下一行。或者您可能想要执行
FOR UPDATE SKIP LOCKED
来确定下一个要处理的
ObjectID


0
投票

我们遇到了类似的问题,多个 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 将锁定下一组行,而不是等待第一个会话释放锁

© www.soinside.com 2019 - 2024. All rights reserved.