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

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

DB2 V8以后陆续引入了三个注册表变量:DB2_EVALUNCOMMITTED、DB2_SKIPDELETED、DB2_SKIPINSERTED,为什么要引入这三个变量呢?在DB2没有这三个变量前,如果一个用户正在更改一行数据,那么DB2会在这一行加上排他锁,别的用户不能访问,除非使用UR隔离级别。

DB2为了改善应用程序并发性,从DB2 V8以后就陆续引入了这三个变量。这三个变量并不会改变锁的本质,只不过通过了解它们的工作方式和机制可以使我们根据我们的业务逻辑来合理的设置调整以提高应用程序并发性。这三个变量改变加锁的时机 , 减少锁冲突 ( 这样其行上的 insert/update/delete 操作不会锁住未命中的 select 操作 )

  db2set DB2_EVALUNCOMMITTED=ON

   db2set DB2_SKIPDELETED=ON    db2set DB2_SKIPINSERTED=ON

?

注解:

  1. db2set DB2_EVALUNCOMMITTED=ON - 这个参数将在记录锁之前进行谓词检查,尽量减少锁的时间
  2. db2set DB2_SKIPINSERTED=ON - 这个参数将新 insert 且没有提交的数据跳过;例如,SELECT/UPDATE 语句不会发现这条记录
  3. db2set DB2_SKIPDELETED=ON - 这个参数将新 delete 且没有提交的数据跳过;例如,SELECT/UPDATE 语句不等待这条记录的提交,并且认为他已经被删除了

下面我们通过实验来演示这三个变量是如何影响SQL语句的行为的:


表 4. 实验使用的数据模型

表名 表结构 数据
TEST 表结构 包含如下 10 行数据:
'1', '1'、'2', '2'、'3', '3'、'4', '4'、'5','5'、'6','6'、'7','7'、'8', '8'、'9','9'、'10','10'

?

在 session1 中插入一条记录 ('11','11') 到 TEST 表中,见图 30:


图 30. 执行 INSERT 事务的 session1 窗口
图 30. 执行 INSERT 事务的 session1 窗口

在 session2 中根据条件 a= ’ 1 ’ or a= ’ 11 ’查询 TEST 表,session2 被锁住,为什么呢?这是因为 session1 中 INSERT 事务还没有 COMMIT,所以这个时候 session 2 处于锁定等待 状态,见图 31、图 32:


图 31. 执行查询事务的 session2 窗口
图 31. 执行查询事务的 session2 窗口

正常情况下上述事务的加锁机制就是这样的,但是有时候用户希望这个时候能够查询到数据,那么怎么解决这个矛盾呢?下面我们来仔细讲解这三个变量。

设置 DB2_EVALUNCOMMITTED

DB2 V8.1.4 版本中首次引入了 DB2_EVALUNCOMMITTED 这个 DB2 注册表变量。当它被启用(=TRUE | ON | YES | 1)时,它将修改 DB2 中只读查询的行为,使之允许在索引扫描(必须是 Type 2 索引)或表访问时推迟锁,直到限定语句的所有谓词都是已知的。引入这个新的注册表变量是为了可选地提高一些应用程序的并发性,其实质是允许读扫描推迟或避免行锁,直到适合特定查询的一个数据记录成为已知。

在 DB2 V8.1.4 之前(并且没有设置这个注册表变量),DB2 将执行保守式的锁:在验证行是否满足查询的排除谓词之前,它将锁定每个被访问的行,不管数据行是否被提交落实,以及根据语句的谓词它是否被排除,对于索引扫描和表访问都执行这样的锁定操作。

下面我们举一个简单的例子来演示:


表 5. 实验使用的数据模型

表名 表结构 数据 备注
TEST 表结构 包含如下 10 行数据:
'1', '1'、'2', '2'、'3', '3'、'4', '4'、'5','5'、'6','6'、'7','7'、'8', '8'、'9','9'、'10','10'
暂时未建索引

?

现在有两个session发出了下面的SQL语句,见图33、图34:


图 33. 执行 INSERT 事务的 session1 窗口
图 33. 执行 INSERT 事务的 session1 窗口

我们查看session 2的状态,可以看见session2处于锁定等待 状态,见图 35:


图 35. 快照监控窗口
图 35. 快照监控窗口

session1 执行 DB2 INSERT INTO TABLE TEST VALUES ('11', '11') 将阻塞所有其他的扫描器扫描它,因为它持有行上的 X 锁,所以 session2 执行 DB2 SELECT * FROM TEST 将被阻塞,直到 session1 提交或回滚。但是我们假设 session2 执行的语句是 DB2 SELECT * FROM TEST WHERE id='10' ,在此情况下,即使 session2 中事务 与 session1 中事务未提交的任何值没有关系,它也仍将被阻塞,处于锁定等待 状态。在 DB2 中,默认情况下将发生这一系列的事件,因为默认的隔离级别是 CS,这种隔离级别表明,一个查询访问的任何一行在游标定位到该行时都必须被锁定。在 session1 释放它用于更新(INSERT)表 TEST 的锁之前,session2 不能包含表 TEST 第一行上的锁。如果 DB2 事先能够知道值 A='11' 不是 session2 的数据请求的一部分(换句话说,它在锁行之前计算了谓词),就可以避免阻塞,这是合情合理的,现在 DB2 数据库已经支持这种行为,通过启用 DB2_EVALUNCOMMITTED 注册变量实现,该实例变量设置后需要重启实例,见图 36:


图 36. 设置实例注册表变量 DB2_EVALUNCOMMITTED