日期:2014-05-16  浏览次数:20405 次

关于DB2 数据库并发性(3)

?

乐观锁定是与悲观锁定相对应的,所以要谈乐观锁定,就不可避免的要谈谈什么是悲观锁定,什么是乐观锁定?

?

所谓悲观锁定,就是查询表之后和尝试搜索的更新或删除记录操作之间的时间段挂起锁定的一种锁定策略。我们假设有这样一个场景,见图 56:



悲观锁定策略是:用户使用 RR/RS 隔离级别执行 SELECT 操作或以排他 exclusive 模式锁定表并执行查询操作(这里举例说明),通过游标得到一个结果集——对结果集记录加锁或者锁表,然后遍历这个游标,对这个结果集的每条记录做进一步的判断,符合条件的做更新操作,不符合条件的跳过,一直到游标遍历结束后,执行 COMMIT 操作,释放由 SELECT 操作获得的锁以及更新操作获得的锁,见图 57:



图 57. 悲观锁定策略

悲观锁定的优点就是能够保证实现一致且安全的更改。但是这种锁定策略的主要缺点就是在数据处理期间,一直占据着资源——被处理的资源的锁生命周期比较长,这样的话,可能会降低数据库的并发性,对于具有大并发用户的系统,需要等待资源释放的概率则会增加。

?

乐观锁定 是一项技术,它用于在选择 (SELECT) 行与更新或删除行之间未拥有行锁定的数据库应用程序。应用程序乐观地假定在更新或删除操作前未锁定的行不可能更改。如果行更改,那么更新或删除操作将失败,并且应用程序逻辑将通过重试查询操作(此处是举例说明)来处理这种故障。乐观锁定策略的主要优点是最小化给定资源对于其他事务的不可用时间,因此,它具有比悲观锁定更好的并行性,因为其他应用程序可以读写该行。它的一个缺点是应用程序中需要有更多的重试逻辑。

基于上述同样的场景,乐观锁定的策略是用户使用 CS/RS 隔离级别执行 SELECT 操作(此处是举例说明),得到一个结果集存放到客户端,然后执行 COMMIT 操作,释放对资源的锁定,然后在客户端遍历这个结果集,对这个结果集中的每条数据做进一步的判断,符合条件的做更新操作(如果确实这一行已经被修改了,那么更新操作将失败,应用程序逻辑将处理这些失败,例如,重新尝试查询),不符合条件的跳过,一直到遍历结束,执行 COMMIT 操作,释放相关资源上的锁,见图 58:



图 58. 乐观锁定策略

DB2 V9.5 之前版本的 DB2 应用程序只能通过构建搜索式 UPDATE 语句启用按值乐观锁定,该语句查找具有与所选值完全相同的值的行。如果行的列值已更改,那么搜索式 UPDATE 语句将失败。但是,按值乐观锁定具有一些缺点:

  • 标识主动错误信息,这可能会更新错误的行
  • 构建 UPDATE 搜索条件对应用程序来说很复杂
  • DB2 服务器根据值来搜索目标行的效率不高
  • 某些客户机类型与数据库类型之间的数据类型不匹配,例如,时间戳记不允许在搜索式 UPDATE 中使用所有列。

?

在 DB2 V9.5 之前,乐观锁定是按值乐观锁定,完全由应用程序本身逻辑控制实现的,而从 DB2 V9.5 开始支持增强的乐观锁定,除了应用程序本身逻辑控制实现之外,DB2 本身还提供了一些增强的机制来提高执行的效率。

DB2 V9.5 增加了速度更快的乐观锁定的支持,这种乐观锁定不会产生主动错误信息(误判),

DB2 V9.5 的乐观锁定特性最小化了给定资源对于其他事务的不可用时间,进一步改善了并发性。这一支持通过如下所示的新 SQL 函数、表达式和特性实现的:

  • 行标识符(RID_BITRID)内置函数:该内置函数可用于 SELECT 结果列表或谓词语句。例如,在谓词 WHERE RID_BIT(tab)=? 中,RID_BIT 等于谓词被实现为一种新的直接访问方法(避免了表扫描),从而可以更有效地定位行。在以前,被称为值乐观锁定的技术确定值的方式为:将所有选择(SELECT)的列值添加到谓词,然后应用某些惟一的列组合来筛选出单个行,这种通过表扫描方式访问方法效率较低。
  • ROW CHANGE TOKEN表达式:这种新的表达式返回一个标记作为 BIGINT 。这个标记表示某一行的修改序列中的一个相对点。应用程序可以将某行的当前行更改标记值与上次取回行时保存的行更改标记值进行比较,以判断行是否发生修改。表需要定义一个行修改时间戳列来保存时间戳值,是否提供行修改时间戳列将影响乐观锁定的行为,因为该列有助于将行更改标记的粒度从页级别提高到行级别,这对乐观锁定应用程序非常有利。

使用这种编程模型的应用程序将从增强的乐观锁定特性中获益。

?

在 DB2 V9.5 中,对普通的表即可使用针对乐观锁定的新 SQL 表达式和属性,但是,如果对普通的表不进行 DDL 修改的情况下,乐观锁定应用程序可能会产生更多的漏判。因此,要避免发生漏判,执行乐观锁定的目标表应执行以下任意一种操作来增加 ROW CHANGE TIMESTAMP 列:

  • 创建表时定义 ROW CHANGE TIMESTAMP 列
  • 对表进行修改以包含 ROW CHANGE TIMESTAMP 列

要在应用程序中启用增强的乐观锁定支持,需要执行以下基本步骤:

  • 在初始查询中,对要进行处理的所有行的行标识符和行更改标记执行 SELECT(使用 RID_BIT() 和 RID() 内置函数),使它们被包含在查询列表中。
  • 释放行锁定,以便其他应用程序可以对表执行选择、插入、更新和删除操作。
  • 通过在搜索条件中使用行标识符和行更改标记对目标行执行搜索式 UPDATE 或 DELETE,并乐观地假定在执行原始查询语句后未锁定的行尚未更改。
  • 如果行已更改,那么更新操作将失败,并且应用程序逻辑必须处理该故障。例如,应用程序将重试查询和更新操作。
注:行标识符、行更改标记的用途如下
        —搜索行标识符以直接访问目标行,而不是通过全表扫描或者索引扫描
        —搜索行更改标记以确认行的状态,即如果行更改标记没变,表明相应记录未被任何事务更改,
        反之表明记录已经被更改。
			

?

针对乐观锁定设计并已启用乐观锁定的应用程序,将按照下列操作顺序向数据库发送请求:



?

 
SELECT SALARY, row change token FOR STAFF, RID_BIT(STAFF) 
 INTO :h_SALARY, :h_rct, :h_rid 
 FROM STAFF WHERE ID = 240

?

在此方案中,应用程序首先读取所需的每行。我们准备在应用程序中使用乐观锁定策略,所以选择列表包括保存在 :h_rid 主变量中的行标识符值和保存在 :h_rct 主变量中的行更改标记值,见清单1

在启用了乐观锁定的情况下,应用程序乐观地假定更新或删除操作的任何目标行都保持不变。为了提高数据库并行性,应用程序使用下列其中一种方法除去行锁定:

  • 落实工作单元,在这种情况下行锁定将被除去
  • 使用 WITH RELEASE 子句关闭游标,在这种情况下行锁定将被除去
  • 使用较低的隔离级别:
    • 游标稳定性(CS),在这种情况下,在游标访存到下一行或结果表末尾后行未锁定。