图 6-5 阐释了 DB2 V9.5 的乐观锁定特性的功效。事务 1 和事务 2 同时读取相同的行,包括 RID_BIT 和 ROW CHANGE TOKEN 值。随后,事务 1 在执行完SELECT并确保该行未发生修改后,通过将
RID_BIT 和 ROW CHANGE TOKEN谓词添加到UPDATE语句对行进行更新。现在,当事
务 2 尝试使用与事务 1 相同的谓词对同一行进行更新时,它无法查找到该行,因为ROW CHANGE
TOKEN的值已经根据事务 1 的UPDATE进行了更改。事务 2 必须进行重试更新,以取回最新的数
据。
图 6-5 DB2 V9.5 的乐观锁定特性
启用乐观锁定特性
由于不需要对表进行 DDL 修改即可使用针对乐观锁定的新 SQL 表达式和属性,因此可以轻松地在您的测试应用程序中尝试乐观锁定特性。
注意,在不修改表结构增加 ROW CHANGE TIMESTAMP 列的情况下,乐观锁定应用程序可能会产生更多的误判 (False Positive) 。在生产环境中,如果应用程序发生了误判 (False Positive),就不能够实现较好的并发,因为误判很可能造成大量重试操作。因此,要避免发生误判,执行乐观锁定的目标表应执行以下任意一种 DDL 操作:
? ?
创建时定义 ROW CHANGE TIMESTAMP 列 修改以增加 ROW CHANGE TIMESTAMP 列
当在应用程序中希望直接启用乐观锁定支持而不愿意修改表结构时,只需要执行以下基本步骤即可: (1) 在初始查询中,对要进行处理的所有行的行标识符和行修改标记执行SELECT( 使用RID_BIT()
和 RID()内置函数 ) 。
(2) 释放行锁,以便其他应用程序可以对表执行SELECT、INSERT、UPDATE 和 DELETE( 例如,使用游标稳定性 (CS) 隔离级别或未提交读 (UR) 隔离级别 ) 。
(3) 对目标行执行可搜索的UPDATE或DELETE,在搜索条件中使用行标识符和行修改标记,乐观地假定自从执行最初的SELECT语句后,未锁定的行没有发生过修改。
(4) 如果行发生了修改,UPDATE操作将失败,应用程序逻辑必须处理这一失败。例如,应用程序将重试SELECT 和 UPDATE操作。
运行以上步骤之后,如果存在下述情况,则可以考虑更新表结构增加 ROW CHANGE TIMESTAMP 列:
? 如果应用程序执行重试的次数超过预期值或与预期值相同,那么向表添加一个 ROW CHANGE TIMESTAMP 列,以确保RID_BIT函数对行标识符作出的修改只会使行修改标记无效,而同一数据页上的其他活动不受影响。
? 要查看给定时间范围内执行了插入或更新操作的行,需要创建或修改表以包含一个 ROW CHANGE TIMESTAMP 列。该列由数据库管理器自动维护,并可以通过列名或ROW CHANGE
TIMESTAMP表达式进行查询。
对于 ROW CHANGE TIMESTAMP 列 ( 只针对这种列 ),如果该列使用IMPLICITLY HIDDEN属性定义,那么当对表列进行隐式引用时,不会对该列执行外部化。然而,在 SQL 语句中,可以始终显式引用一个隐式隐藏的列。当向表添加列可能造成使用隐式列列表的应用程序失败时,这一特性非常有用。 行修改标记的粒度和误判
RID_BIT()内置函数和行修改标记是实现乐观锁定的唯一需求。然后使用 ROW CHANGE
TIMESTAMP 列会促使 DB2 服务器保存最后一次修改 ( 或第一次插入 ) 行的时间。这提供了一种方式捕获最近一次修改行的时间戳。可以通过以下任意一条语句定义行修改时间戳列:
? ?
GENERATED ALWAYSFOR EACH ROW ON UPDATE AS ROW CHANGE TIMESTAMP 该时间戳列始终由数据库管理器维护。
GENERATED BY DEFAULTFOR EACH ROW ON UPDATE AS ROW CHANGE TIMESTAMP 该时间戳列默认情况下由数据库管理器维护,但是也接受用户提供的输入值。
当应用程序对表使用新的 ROW CHANGE TOKEN 表达式时,需要考虑以下两种可能性:
? 表没有定义行修改时间戳列:ROW CHANGE TOKEN 表达式返回一个派生的 BIGINT 值,由同一页面中的所有行共享。如果页面中的某行被更新,那么将针对该页面中的所有行修改行修改标记。这意味着对其他行进行修改时更新会失败,这一特性被称为误判。
注意:
只有在应用程序可以容忍误判,并且不希望向每一行添加针对 ROW CHANGE TIMESTAMP 列的额外存储的情况下,才使用这种模式。
? 表具有一个行修改时间戳列:ROW CHANGE TOKEN 表达式返回一个从列的时间戳值获得的 BIGINT 值。在这种情况下,发生误判的几率大大减少。如果对表进行了重组和重新分布,那么当移动某行并且应用程序使用以前的RID_BIT()值时,将发生误判现象。
可通过以下SELECT 语句检查行修改时间戳列是否存在, SELECT 查询输出如下所示:
SELECT COLNAME, ROWCHANGETIMESTAMP, GENERATED FROM SYSCAT.COLUMNS WHERE TABNAME='tablename' AND ROWCHANGETIMESTAMP='Y' COLNAME ROWCHANGETIMESTAMP GENERATED ------------ ------------------ --------- ROWCHGTS Y A
在上述输出中,存在一个行修改时间戳列 ROWCHGTS,并通过 GENERATED ALWAYS 子句定义 ( 值“ A ”表示 GENERATED ALWAYS,而值“ D ”表示 GENERATED BY DEFAULT) 。 基于时间的更新检测
某些应用程序需要了解特定时间范围内的数据库更新,以便进行数据复制、场景审计等等。这可以通过包含行修改时间戳列的表实现,通过定义行修改时间戳列来保存ROW CHANGE TIMESTAMP表达式生成的时间戳值。这种新的ROW CHANGE TIMESTAMP表达式返回的时间戳表示最后一次进行行修改的时间,使用类似于CURRENT TIMESTAMP的本地时间表示。对于已经更新过的行,将反映对行执行的最近更新。否则,该值将对应于最初的行插入时间。 自ALTER TABLE语句之后未进行更新的行将返回列的类型默认值,该值为 0001 年 1 月 1 日午夜。只有进行过更新的行才具有唯一的时间戳。使用离线表重组对时间戳进行具体化的行将返回一个唯一的时间戳,该时间戳在表重组期间生成。REORG仅仅使用INPLACE选项无法满足需求,因为它没有对模式修改进行具体化。 下面是一些基于时间的更新检测示例, 具有行修改时间戳列的表如下所示: CREATE TABLE EMPLOYEE (EMPNO CHAR(6) NOT NULL, ...... ROWCHGTS TIMESTAMP NOT NULL GENERATED ALWAYS FOR EACH ROW ON UPDATE AS ROW CHANGE TIMESTAMP) 对于未定义行修改时间戳列的表,可稍后通过下面这条 ALTER TABLE 语句添加,如下所示: ALTER TABLE EMPLOYEE ADD COLUMN ROWCHGTS TIMESTAMP NOT NULL GENERATED ALWAYS FOR EACH ROW ON UPDATE AS ROW CHANGE TIMESTAMP 选择在最近 30 天内发生修改的所有行的 SQL 语句如下所示: SELECT * FROM EMPLOYEE WHERE ROW CHANGE TIMESTAMP FOR EMPLOYEE <= CURRENT TIMESTAMP AND ROW CHANGE TIMESTAMP FOR EMPLOYEE >= CURRENT TIMESTAMP - 30 days
表 6-6 展示了在创建具有行修改时间戳列的表后,使用 INSERT、IMPORT 或 LOAD 填充后的 ROW CHANGE TIMESTAMP 列的内容。
表 6-6 INSERT、IMPORT 或 LOAD 填充后的 ROW CHANGE TIMESTAMP 列的内容
EMPNO 000010 … 000030
FIRSTNME CHRISTINE … SALLY
LASTNAME HAAS … KWAN
PHONENO 3978 … 4738
ROW CHANGE TIMESTAMP 2008-12-20 13:53:01.296000 …
2008-12-20 13:53:01.312001
表 6-7 展示了将行修改时间戳列添加到现有表后,ROW CHANGE TIMESTAMP 列的内容。
表 6-7 行修改时间戳列添加到现有表后,ROW CHANGE TIMESTAMP 列的内容
EMPNO 000010 … 000030
FIRSTNME CHRISTINE … SALLY
LASTNAME HAAS … KWAN
PHONENO 3978 … 4738
ROW CHANGE TIMESTAMP 0001-01-01 00:00:00.000000 …
0001-01-01 00:00:00.000000
隐式隐藏列 这一特性有利于将行修改时间戳列应用到现有表和应用程序中。CREATE或ALTER TABLE语句中的IMPLICITLY HIDDEN属性表示:除非根据名称显式引用列,否则该列在 SQL 语句中不可见。例如,假设某个表包含一个使用IMPLICITLY HIDDEN子句定义的列,SELECT *操作的结果将不会包含隐式隐藏的列。然而,如果SELECT显式引用隐式隐藏列的名称,那么它将在结果表中包含该列。只有 ROW CHANGE TIMESTAMP 列才能使用IMPLICITLY HIDDEN 属性。 行修改时间戳的隐式隐藏列的声明方法如下: CREATE TABLE SALARY_INFO ( LEVEL INT NOT NULL, SALARY INT NOT NULL, UPDATE_TIME TIMESTAMP NOT NULL IMPLICITLY HIDDEN GENERATED ALWAYS FOR EACH ROW ON UPDATE AS ROW CHANGE TIMESTAMP) 或 ALTER TABLE SALARY_INFO ADD COLUMN UPDATE_TIME TIMESTAMP NOT NULL IMPLICITLY HIDDEN GENERATED ALWAYS FOR EACH ROW ON UPDATE AS ROW CHANGE TIMESTAMP 使用 DESCRIBE 命令显示表列的结果输出,如下所示: DESCRIBE TABLE SALARY_INFO Data type Column Column name schema Data type name Length Scale Nulls ----------- --------- ------------------- ---------- ----- ------ LEVEL SYSIBM INTEGER 4 0 No SALARY SYSIBM INTEGER 4 0 No UPDATE_TIME SYSIBM TIMESTAMP 10 0 No
针对隐式隐藏的列执行 INSERT 和 SELECT 操作,如下所示: INSERT INTO SALARY_INFO VALUES (1, 50000) SELECT * FROM SALARY_INFO LEVEL SALARY ----------- ----------- 1 50000 可以通过下面的 SQL 语句显式引用隐式隐藏列的 INSERT 和 SELECT: INSERT INTO SALARY_INFO (LEVEL, SALARY, UPDATE_TIME) VALUES (2, 30000, DEFAULT) SELECT LEVEL, SALARY, UPDATE_TIME FROM SALARY_INFO WHERE LEVEL = 2 LEVEL SALARY UPDATE_TIME ----------- ----------- -------------------------- 2 30000 2008-12-18-15.34.24.437000
乐观锁定特性的局限性和注意事项
? ? ? ? ?
不能指定为 ROW CHANGE TIMESTAMP 的列包括:主键、外键、多维聚合 (MDC) 列、范围分区 (RANGE PARTITION) 列、数据库哈希分区键、DETERMINED BY 约束列和别名。 DPF 配置不支持RID()函数。
在乐观锁定场景中,在取回到更新操作期间执行在线或离线表REORG可能会造成更新失败,但是普通的应用程序重试逻辑应该能够处理。
在 DB2 V9.5 中,IMPLICITLY HIDDEN属性只能应用于 ROW CHANGE TIMESTAMP 列以实现乐观锁定。
对于后来才添加 ROW CHANGE TIMESTAMP 列的表,在保证所有行已被具体化之前,
INPLACE REORG的使用会受到限制 ( 返回错误代码 SQL2219, REASON CODE=13) 。这可通过LOAD REPLACE命令或典型的表REORG实现。这将防止发生误判,具有 ROW
CHANGE TIMESTAMP 列的表没有限制。
6.3.3 乐观锁应用案例
上面我们讲解了乐观锁的原理和实现技术,现在我们来举一个乐观锁的应用案例。假设某员工履行新的工作职责并被调到另一个部门工作。公司的两名经理 ( 原部门的经理 MANAGER1 和新部门的经理 MANAGER2) 正在使用人事管理应用程序更新 SAMPLE 数据库的 EMPLOYEE 表中的员工记录。此时存在一种可能,即两名经理可能同时对该员工的记录进行更新。当 MANAGER1 选择并更新这条员工记录时,MANAGER2 也对同一条记录执行更新。这时,乐观锁定特性将发挥作用,例如,它使 MANAGER2 在使用当前应用程序更新时了解到某个特定记录已经被更新过。这样,应用程序可以更容易地执行指令,因为它不必实现自己的更新检测逻辑。下面我们讲述几个常见的应用场景: 场景 1
EMPLOYEE 表包含一个隐式隐藏的 ROW CHANGE TIMESTAMP 列 ( 后来添加的 ),并且只有 MANAGER1 访问了该表。 MANAGER1 从 EMPLOYEE 表中选择数据,并在稍后尝试将“ Christine Haas ”的电话号码 3978 更新为 1092 。更新成功。这个场景的主要步骤如下:
百度搜索“77cn”或“免费范文网”即可找到本站免费阅读全部范文。收藏本站方便下次阅读,免费范文网,提供经典小说教育文库DB2隔离级别和锁 - 图文(5)在线全文阅读。
相关推荐: