【问题标题】:DB2 Trigger Row LockingDB2 触发器行锁定
【发布时间】:2014-02-11 21:57:01
【问题描述】:

我想在 DB2 10.1.2(在 Linux 上运行的 LUW)中创建一个触发器,当一个字段更新为某个值时,该表中的行数会使用该值进行计数,以查看它是否与计数匹配在另一个表中,然后更新该表(例如,将任务状态汇总为作业状态)。表达起来似乎很容易:

CREATE TRIGGER AUTHORED
  AFTER UPDATE OF TASK_STATUS ON TASK_TABLE
  REFERENCING NEW AS N FOR EACH ROW WHEN (TASK_STATUS = 'Completed')
    update JOB_TABLE set JOB_STATUS='Completed'
      where JOB_TABLE.ID = N.JOB_ID
        and JOB_TABLE.TOTAL_TASKS = (select count(*) from TASK_TABLE
          where TASK_TABLE.JOB_ID = N.JOB_ID
            and TASK_TABLE.TASK_STATUS = 'Completed')

不幸的是,触发器的上下文和触发器的主体似乎不在同一个工作单元中,当您从触发的更新本身计算锁定的行时,就会发生死锁。这是我执行触发更新后“db2pd -wlocks”的输出:

Locks being waited on :
AppHandl [nod-index] TranHdl    Lockname                   Type       Mode Conv Sts CoorEDU    AppName  AuthID   AppID
44248    [000-44248] 111        0200040E070000000000000052 RowLock    ..X       G   1385       perl     KJPIRES  10.0.15.139.38727.140201011731
14937    [000-14937] 15         0200040E070000000000000052 RowLock    .NS       W   1238       perl     KJPIRES  10.0.15.139.55287.140211231609

我尝试使用“with UR”作为内部计数,但在创建触发器时显式忽略了这一点(“SQL20159W 由于语句上下文而忽略了隔离子句。SQLSTATE=01652”)。

我也尝试使用 BEFORE 和 INSTEAD OF,但遇到了问题。

这似乎很常见。平时是怎么处理的?

【问题讨论】:

  • 你试过'for each statement'吗?
  • 我刚刚尝试了“for each statement”,但它仍然陷入僵局。 (我不确定如果更新更新多行并且我执行“for each statement”而不是“for each row”会发生什么,但它无论如何都不起作用。)
  • 如您所说,触发器不可能在单独的 UOW 中执行,因为触发器主体实际上已编译到导致该触发器运行的 UPDATE 语句的计划中。如何判断是否存在死锁?
  • 这是 DB2 for LUW、i 还是 z/OS?
  • 我们在这里查看的行数是多少?在表格中,我的意思是,不是工作单元。 JOB_STATUS 可以在常规读取时即时派生(例如,通过定义视图)吗?在最坏的情况下,您可以定义一个计划调用来检查所有作业。也许是一个首先提交任务状态的存储过程?指定SKIP LOCKED DATA 在这里可以工作吗?

标签: sql triggers db2


【解决方案1】:

我认为它没有陷入僵局。首先,DB2 有一个死锁检测器,如果有两个连接锁在不同的资源上等待另一个,DB2 将杀死其中一个。我没有看到两个连接。

其次,您正在尝试根据定义触发器的表 (TASK_TABLE) 中的值 (COUNT) 更新另一个表 (JOB_TABLE)

CREATE TRIGGER AUTHORED
 AFTER UPDATE OF TASK_STATUS ON TASK_TABLE
 REFERENCING NEW AS N
 FOR EACH ROW WHEN (TASK_STATUS = 'Completed')
  update JOB_TABLE
   set JOB_STATUS='Completed'
   where JOB_TABLE.ID = N.JOB_ID
   and JOB_TABLE.TOTAL_TASKS = (       -- Total tasks before or after the update?
    select count(*)
    from TASK_TABLE
    where TASK_TABLE.JOB_ID = N.JOB_ID
    and TASK_TABLE.TASK_STATUS = 'Completed'
   )

您是否在触发器之外运行更新。行不行?

我认为事务正在修改未提交的行(更新 task_table),当您在触发器中发出 select 时,它会等待同一张表上的提交。实际上,事务不知道被更新的行是否处于“完成”状态,并且您有一个谓词来寻找哪一个更新:并且 JOB_TABLE.TOTAL_TASKS = x,但当前行可能是该集合的一部分。

你要更新哪一个? JOB_TABLE.TOTAL_TASKS 在当前更新之前,还是在当前更新之后?最后,我不确定您为什么对此有谓词。

顺便说一句,不要使用count(*),而是使用count(0)

【讨论】:

  • 当您在触发器中发出 select 时 -- 不,它没有。
  • 还有,为什么不用count(*)而是用count(0)
  • 如果优化级别较低,将获取所有行。
  • 这是假设 LUW。
  • 原始更新是将 TASK_STATUS ON TASK_TABLE 修改为“已完成”,这是触发器的要求。 JOB_TABLE.TOTAL_TASKS 永远不会被修改:它仅用于查看“已完成”任务的总数是否与该数字匹配。另外,我将 count(*) 更改为 count(0) 无效。
【解决方案2】:

上面的触发器没有任何问题。我不知道,我的 JOB_TABLE 上的未结交易未完成。我追踪了它,回滚并完成了触发更新。

我将使用count(0) 而不是count(*) 作为建议的优化。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多