使用该FOR UPDATE
子句编码的游标能否防止并发事务读取游标当前所在的行?
否 - 在隔离级别 CS 的情况下,Db2 将在当前行上持有一个 U 锁,这与可能需要的 S 锁兼容(请参阅后面关于 CURRENTDATA 绑定参数的评论以及它对避免读者使用 S 锁的影响)。
该UPDATE ... WHERE CURRENT OF
语句是否检测到在打开游标之后和从 CURSORs 结果集中获取更新行之前何时更改了更新的行?
否 - 对于隔离级别 CS,Db2 在读取行之前不会获取锁。如果在OPEN CURSOR
需要不同的隔离级别后要求数据保持不变。
但这让我感到困惑,因为它不会防止丢失更新现象(当允许并发进程在第一个进程中的更新完成之前读取值时,它会继续使用旧值进行处理并覆盖第一个进程的更新通过 CURRENT OF CURSOR 更新的进程)。
假设两个事务都在使用FOR UPDATE
并且UPDATE ... WHERE CURRENT OF
这种情况不会发生。每次读取都会尝试获取 U 锁。由于 U 锁彼此不兼容,因此第二次读取将等待第一个 U 锁被释放。(https://www.ibm.com/docs/en/db2-for-zos/12?topic=locks-lock-modes-compatibility)
对于更复杂的情况,其中一个(或两个)事务未使用FOR UPDATE
并且UPDATE ... WHERE CURRENT OF
有可能发生丢失更新现象。
很久以前,Db2 引入了绑定参数 CURRENTDATA 来帮助控制这种行为。
- CURRENTDATA(NO)(从 Db2 10 开始默认)- 尽可能避免锁定,但会增加获取非当前数据的风险
- CURRENTDATA(YES) - 获取 S 锁以降低获取非当前数据的风险。重要的是要注意 CURRENTDATA(YES) 并不能完全消除非当前数据的风险。
Db2 手册 - 选择 CURRENTDATA 选项
Gareth 在这方面有一些很棒的文章,其中包含更多详细信息 - Db2 for z/OS Locking for Application Developers Part 8
为了完全防止丢失更新的风险,一个好的方法是添加谓词以确保更新仅针对预期数据发生。Gareth 在他关于锁定的博客的第 9 部分中为此提供了三个选项。一般的想法是拥有像更新时间戳这样的东西,当行的任何部分被更新时,它总是被更新。WHERE
然后在语句的子句中包含一个谓词,UPDATE
以确保仅当更新时间戳与最初读取该行时的时间戳相同时才会发生更新。Db2 9 中的ROW CHANGE TIMESTAMP
特性使这种方法更容易。