【问题标题】:Database - synchronized execution of update queries on different records数据库 - 对不同记录同步执行更新查询
【发布时间】:2015-07-29 14:15:21
【问题描述】:

我有一个这样的表结构:

ParentObject(ObjectId 字符串,.....)

ChildObject(ObjectId string, ParentId String, Atribute1 String, State Integer....)

有多个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更新了10条具有相同ParentId的ChildObject记录,则其余具有相同ParentId的ChildObject记录应该由Process1更新,而不是由Process2更新。

由于进程并行运行,一些 ChildObject 记录在一个进程中更新,而某些具有相同 ParentId 的 ChildObject 记录在另一个进程中更新。

Select.. For Update 在我的情况下不起作用,因为更新发生在 ChildObject 表中的不同记录上。

锁定整个 ChildObject 表可能不是一个好的解决方案。

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

谢谢,

瓦纳提

【问题讨论】:

    标签: database oracle synchronization sql-update


    【解决方案1】:

    如何将 ProcessName 列添加到 ParentObject 表。如果 ProcessName 为空,则要首先更新子进程的进程必须在父进程上设置 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
        )
    )
    

    【讨论】:

    • 非常感谢您的及时回复。那确实是最好的解决方案。但是我有一个限制,不能将新列添加到表中。请告诉我是否有其他解决方案?
    • 你能新建一个表吗?如果是这样,您可以使用 ObjectId PK 和 ProcessName 列创建表。唯一的区别是您必须将行插入新表而不是更新它们。
    • 此阶段无法进行任何 DDL 修改。我肯定会在应用程序的下一个版本中使用这种方法。但目前,我必须从代​​码中控制这种行为,并且不允许修改 DDL。
    • 在处理记录时是否更改 ChildObject.state?
    • 是的。在这个更新查询之后,每个进程都会执行一个选择查询来获取所有更新了进程名称的记录并修改状态。
    【解决方案2】:

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

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

    根据 Oracle 版本以及您正在执行的操作,您可能不希望线程阻塞等待无限期地锁定 ParentObject 行。您可能想要执行FOR UPDATE NOWAIT,如果您无法锁定父行,则捕获异常,然后继续下一行。或者您可能想要执行FOR UPDATE SKIP LOCKED 来确定下一个要处理的ObjectID

    【讨论】:

      猜你喜欢
      • 2019-03-12
      • 2014-11-01
      • 2011-04-23
      • 1970-01-01
      • 2020-10-10
      • 2017-02-17
      • 1970-01-01
      • 1970-01-01
      • 2023-03-13
      相关资源
      最近更新 更多