DB2 for z/OS:CURSOR FOR UPDATE 锁定行为

DB2 for z/OS: CURSOR FOR UPDATE locking behavior

我对 z/OS 的 IBM DB2 CURSOR 中的 FOR UPDATE 子句有疑问。 假设隔离级别游标稳定性(BIND 命令中的标准参数)。 DB2 版本是 11.

我的第一个问题是:使用 FOR UPDATE 子句编码的 CURSOR 是否可以防止并发事务读取 CURSOR 当前所在的行? 我的第二个问题是:UPDATE ... WHERE CURRENT OF ... 语句是否检测到在打开 CURSOR 之后和从 CURSOR 结果集中获取更新行之前更改了更新的行?

关于这些问题,我在网上看到了一些相互矛盾的说法。 根据我(当前)的理解,FETCH 操作仅在获取的行上获取更新锁,因此并发事务至少可以读取同一行。 U-Lock 仅在 UPDATE WHERE CURRENT OF CURSOR 实际完成的情况下提升为 X-Lock(取决于应用程序逻辑)。但这让我感到困惑,因为它不会阻止更新丢失的现象(当允许并发进程在第一个进程的更新完成之前读取值时,它会继续使用旧值进行处理并覆盖第一个进程的更新已通过 CURRENT OF CURSOR 更新的进程)。

Can a cursor that is coded with the FOR UPDATE clause prevent concurrent transactions from reading the row on which the cursor is currently positioned?

否 - 使用隔离级别 CS,Db2 将在当前行上持有 U 锁,这与可能需要的 S 锁兼容(请参阅后面关于 CURRENTDATA 绑定参数的评论,它对避免 S 锁的影响读者)。

Does the UPDATE ... WHERE CURRENT OF statement detect when the updated row has been changed after the cursor has been opened and before it has been fetched from the CURSORs result set?

否 - 使用隔离级别 CS,Db2 在读取行之前不会获取锁。如果您要求数据在 OPEN CURSOR 之后保持不变,则需要不同的隔离级别。

But this confuses me, because it then would not prevent a lost update phenomenon (when the concurrent process is allowed to read the value before the update in the first process is done it continues its processing with the old value and overwrites the update of the first process which has updated via CURRENT OF CURSOR).

假设两个事务都使用 FOR UPDATEUPDATE ... WHERE CURRENT OF,这种情况就不会发生。每次读取都会尝试获取 U 锁。由于 U 锁彼此不兼容,第二次读取将等待第一个 U 锁被释放。 (https://www.ibm.com/docs/en/db2-for-zos/12?topic=locks-lock-modes-compatibility)


对于其中一个(或两个)事务未使用 FOR UPDATEUPDATE ... WHERE CURRENT OF 的更复杂情况,可能会发生丢失更新现象。

很久以前,Db2 引入了绑定参数 CURRENTDATA 来帮助控制这种行为。

  • CURRENTDATA(NO)(默认自 Db2 10 起)- 尽可能尝试避免锁定,但会增加获取非当前数据的风险
  • CURRENTDATA(YES) - 获取 S 锁以降低获取非当前数据的风险。重要的是要注意 CURRENTDATA(YES) 并不能完全消除非当前数据的风险。

Db2 manual - Choosing CURRENTDATA Option

Gareth 在这方面有一些很棒的文章,内容更详细 - Db2 for z/OS Locking for Application Developers Part 8

要完全防范丢失更新的风险,一个好的方法是添加谓词以确保仅针对预期数据进行更新。 Gareth 在他关于锁定的博客 Part 9 中为此提供了三个选项。一般的想法是有一个类似于更新时间戳的东西,当行的任何部分被更新时,它总是被更新。然后在 UPDATE 语句的 WHERE 子句中包含一个谓词,以确保仅当更新时间戳与最初读取行时相同时才会发生更新。 Db2 9 中的 ROW CHANGE TIMESTAMP 功能使这种方法更容易。