本文转至:http://book.51cto.com/art/200906/130019.htm
6.4.1 内部方案锁(Internal Plan Lock)
首先,我们先做一个例子,如果输出结果如下所示:
1. C:/>db2 +c update employee set salary=1234 where empno='000100'
2. DB20000I SQL命令成功完成。--注:执行一条新的SQL语句,+C不提交,否则锁消失了
3. C:/>db2 get snapshot for locks on sample-----监控加锁情况
4. 数据库锁定快照
5. 数据库名称 = SAMPLE
6. 数据库路径 = C:/DB2_01/NODE0000/SQL00001/
7. 输入数据库别名 = SAMPLE
8. 挂起的锁定 = 3
9. 当前已连接的应用程序 = 2
10. ……………………………………………….节省篇幅,略…………………………………………………………………………
11. 锁定名称 = 0x53514C4332473133B7F3CE3241
12. 锁定属性 = 0x00000000
13. 发行版标志 = 0x40000000
14. 锁定计数 = 1
15. 挂起计数 = 0
16. 锁定对象名 = 0
17. 对象类型 = 内部方案锁定(Internal Plan Lock)
18. 方式 = S
19. ……………………………………………….节省篇幅,略…………………………………………………………………………
大家知道无论数据库是在处理静态SQL还是动态SQL,DB2的相关组件都需要去访问程序包信息和相应的SQL查询访问计划。那么默认情况下,DB2都会去给相应的Package Cache加内部P锁,以防止其他的什么操作将正在使用的Package删除,毫无疑问的是,这样的一个锁对性能多少会有影响,但影响是很小的。
DB2的变量注册参数里面有个配置参数是DB2_APM_PERFORMANCE。如果这个值被设置为ON,则会启用无包锁定方式。此方式允许全局查询高速缓存运行,而不必使用包锁定,这些锁定是内部系统锁定,可以保护高速缓存的包条目不被除去。无包锁定方式在一定程度上可以提高性能,但它不允许执行某些数据库操作。这些被禁止的操作可能包括:使包无效的操作、使包不起作用的操作、PRECOMPILE、BIND和REBIND。所以,在这里只是让大家知道这个变量和了解程序包高速缓存的内部工作机制,但是不建议大家修改这个值。
在上面的例子中,我们可以看到有一个内部P锁。这是因为无论我们执行的是一条静态SQL语句还是一条动态SQL语句,在执行过程中,都要依次经过语法检查、语义检查、权限检查、查询重写、下推分析等阶段,最后优化器根据统计信息、配置参数、索引等情况为这条SQL语句生成一个成本最优的执行方案。该方案决定了使用什么扫描方式、访问表的顺序、表的连接方式和使用哪个索引。那么为了保证这条SQL语句在下次执行时不再重复上述步骤,数据库会把这条SQL语句的执行方案存放在程序包高速缓存中(PCKCACHE_SZ)中,在存放的时候它首先需要在程序包高速缓存中申请一块内存,然后把执行方案INSERT到该内存中。在申请时它要在这块内存中加一个锁以确保在SQL语句执行期间,这块内存不被别的应用删除(DROP),这就是内部P锁的作用(在Oracle数据库中这种锁叫LATCH。LATCH是对内存加的锁,不是对数据库对象(表、行)加的锁,所以这种锁我们可以忽略)。
6.4.2 内部V锁(Internal Variation Lock)
当执行动态SQL语句时,对SQL的处理会在程序包的缓存中存储一个变量条目,为了保证这个变量条目在事务处理期间的有效性,DB2会为它赋予一个内部V锁。下面我们看一个例子,在一个B/S结构中,前台运行Java应用,应用通过连接池发起动态SQL语句到数据库。假设某个时刻应用执行如下SQL语句:
1. update account set transdate= date(current timestamp) -? day
在这条SQL语句执行期间,我们执行"get snapshot for locks"监控发现如下锁的信息,
1. Application handle = 40
2. Application ID = 0A0B0A49.EB44.020821194232
3. Sequence number = 0001
4. Application name = db2jcc
5. Authorization ID = DB2INST1
6. Application status = Compiling --注:应用程序正在编译
7. Status change time = 08-21-2008 9:52:10.800363
8. Application code page = 819
9. Locks held = 2
10. Total wait time (ms) = 0
11. List Of Locks
12. Lock Object Name = 0
13. Node number lock is held at = 0
14. Object Type = Internal V Lock --注:内部V锁
15. Tablespace Name =
16. Table Schema =
17. Table Name =
18. Mode = S
19. Status = Granted
20. Lock Escalation = NO
21. Lock Object Name = 0
22. Node number lock is held at = 0
23. Object Type = Internal P Lock --注:内部P锁
24. Tablespace Name =
25. Table Schema =
26. Table Name =
27. Mode = S
28. Status = Granted
29. Lock Escalation = NO
30. ---------------------------------略-----------------------------------
通过上面的锁的信息的监控,我们发现存在内部V锁。下面我们来讲解内部V锁的加锁机制。
在"update account set transdate=date(current timestamp) -? day"这条SQL语句传递给优化器编译(COMPILING)时,我们需要为这条SQL语句提供一个编译环境,这个编译环境包括把特殊的寄存器(CURRENT STAMP)变量转变成真正的值;表ACCOUNT没有模式名,需要使用DEFAULT QUALIFIER用作模式;我们使用了DATE函数,需要知道该函数的函数路径(CURRENT FUNCTION PATH);我们使用了参数标记(?),需要把参数标记转变成真正的值。上述的每一个变量都需要一个内部V锁来保证在该SQL执行期间,这些变量不被别的DDL语句修改或者被程序包缓存空间管理算法删除。
6.4.3 内部S锁
DB2中的很多动作实际是作为一个事件来记录的,那么事件的序号的机制保证这些事件是按照一定的正确的顺序来运转。也就是一个特定的代理AGENT只能一次处理一个特定的事件。
在分区数据库环境中,这样的内部S锁通常是用来在执行无效的DDL语句的时候,初始化特定的临时表空间。
在DB2 V7到现在的版本中,这样的内部S锁实际是作用于临时表空间上的S锁。一个特定应用程序都会有自己的一个存在于特定临时表空间里面的临时表,这样的内部S锁作用其上,以控制其不被其他的操作删除了相应的临时表空间。内部S锁通常用在分区数据库环境中。
6.4.4 内部C锁
内部C锁(Internal Catalog Cache Lock)是为编目缓存(CATALOGCACHE_SZ)中某个条目而得到的一个锁,这样的锁用来在编目缓存中参照或者修改任意的条目的保证其完整性和一致性。DB2的编目缓存用来缓存系统编目表的信息,以加速SQL语句的编译速度和缩短SQL语句的编译过程。
从DB2组件设计的角度来看,当编目缓存的条目信息被锁的时候,相应的系统编目表的数据行也必须被锁。
所以我们的内部C锁的格式和常规的表的数据行的锁的格式是一样的,如下所示:
表空间id+数据库对象id+数据行id
只不过在这样的锁的名字中,会有个'C'用来表示这个锁是Catalog Cache锁。"R"用来表示这个锁是数据记录的锁。在一个事务的处理过程中,如果系统编目表的信息被更新,那么相应的编目缓存的条目的锁就是X锁,通常情况下,只会在相应的条目上加S锁。
DB2内部保证在SQL处理的过程中只需要对编目缓存持有S锁。当SQL编译结束,相应的执行段被创建时,DB2的这个相应的编目缓存条目的锁就会被释放。下面我们举一个内部C锁的例子,输出结果如下:
1. C:/>db2 +c create table t2(id int)
2. DB20000I SQL命令成功完成。
在另外一个窗口中使用"get snapshot for locks"监控信息的输出结果,如下所示(部分):
1. ---------------------------------省略-----------------------------------
2. 锁定名称 = 0x0000050004431A00600E407D43 --注:Tablespaceid+Tableid+RID
3. 锁定属性 = 0x00000000
4. 发行版标志 = 0x40000000
5. 锁定计数 = 255
6. 挂起计数 = 0
7. 锁定对象名 = 0
8. 对象类型 = 内部目录高速缓存锁定
9. 方式 = X
10.
11. 锁定名称 = 0x0100000000000000600E407D43 --注:43代表Catalog Cache Lock
12. 锁定属性 = 0x00000000 --注:52代表是行锁(row lock)
13. 发行版标志 = 0x40000000
14. 锁定计数 = 255
15. 挂起计数 = 0
16. 锁定对象名 = 0
17. 对象类型 = 内部目录高速缓存锁定
18. 方式 = X
19. ---------------------------------省略-----------------------------------
20. 锁定名称 = 0x0300000000000000000000004F
21. 锁定属性 = 0x00000000
22. 发行版标志 = 0x40000000
23. 锁定计数 = 2
24. 挂起计数 = 0
25. 锁定对象名 = 0
26. 对象类型 = 内部对象表锁定
27. 方式 = IN
28. 锁定名称 = 0x03001600000000000000000054
29. 锁定属性 = 0x00000000
30. 发行版标志 = 0x40000000
31. 锁定计数 = 255
32. 挂起计数 = 0
33. 锁定对象名 = 22
34. 对象类型 = 表
35. 表空间名 = IBMDB2SAMPLEREL
36. 方式 = Z
37. ---------------------------------省略-----------------------------------
6.4.5 其他内部锁(1)
除了上述我们常见的一些内部锁外,DB2数据库还存在以下几种内部锁:
1. Inplace Reorg Lock
我们会经常对表做REORG ,REOGR操作会将陈旧的RID进行更新。负责控制并发的扫描器会持有这个锁,直到该扫描器发现没有别的应用请求这个旧的RID。然后扫描器就释放这个锁,这时我们就可以对这个行做在线REORG了。当我们的表很大的时候,如果你不厌烦阅读数据库的快照监控信息的话,你会发现REORG总是在等待Inplace Reorg Lock。同时在在线重组期间,数据库还会产生一个内部改变锁定,来对重组的表进行碎片整理。下面我们举一个Inplace Reorg Lock的例子,输出结果如下:
1. C:/>db2 +c reorg table DB2ADMIN.dept1 inplace allow read access
2. DB20000I REORG命令成功完成。
DB21024I 此命令为异步的,可能未能立即生效。
在另外一个窗口中使用而"get snapshot for locks"监控信息输出,结果如下(部分):
1. 锁定列表
2. 锁定名称 = 0x0300050002000000000000006A
3. 锁定属性 = 0x00000000
4. 发行版标志 = 0x40000000
5. 锁定计数 = 1
6. 挂起计数 = 1
7. 锁定对象名 = 2
8. 对象类型 = 原地重组锁定(inplace reorg lock)
9. 表空间名 = IBMDB2SAMPLEREL
10. 表模式 = DB2ADMIN
11. 表名 = DEPT1
12. 方式 = IN
13. 锁定名称 = 0x03000500000000000000000074 --注:0300代表表空间id;0500表示表ID
14. 锁定属性 = 0x00000000
15. 发行版标志 = 0x40000000
16. 锁定计数 = 1
17. 挂起计数 = 1
18. 锁定对象名 = 5
19. 对象类型 = 内部表改变锁定
20. 表空间名 = IBMDB2SAMPLEREL
21. 表模式 = DB2ADMIN
22. 表名 = DEPT1
23. 方式 = X
24.
25. 锁定名称 = 0x03000500000000000000000054
26. 锁定属性 = 0x00000000
27. 发行版标志 = 0x40000000
28. 锁定计数 = 1
29. 挂起计数 = 1
30. 锁定对象名 = 5
31. 对象类型 = 表
32. 表空间名 = IBMDB2SAMPLEREL
33. 表模式 = DB2ADMIN
34. 表名 = DEPT1
35. 方式 = S
内部L锁
Internal Long/LOBLock(L-lock)负责对大对象进行处理。
内部联机备份锁OLB-LOCK
当我们执行数据库在线备份时,会在表空间级别获得OLB-LOCK锁。它主要处理在线备份期间我们对数据库的更改。
内部O-Lock
Internal-Object Table主要负责提交(COMMIT)同步。
自动调整大小锁定
在DB2 V9中很多数据库配置参数默认都被设置为AUTOMATIC,当我们执行某个特定操作时,DB2会执行自动调整大小锁定。例如,当我们执行备份时,DB2会自动调整UTIL_HEAP_SZ内存大小。
自动存储器锁定
在DB2 V8.2.2以后,如果我们建数据库时使用自动存储(AUTOMATIC STORAGE),那么在我们做某些操作时,数据库就会有自动存储锁定。
6.4.5 其他内部锁(2)
下面我们举一个例子,这个例子中包含了内部联机备份锁、自动调整大小锁和自动存储器锁三种内部锁。输出结果如下:
1. C:/>db2 backup db sample online-注:sample数据库为自动存储,DB2版本是V9.5
在另外一个窗口中使用"get snapshot for locks"监控信息输出,结果如下(部分):
1. 应用程序句柄 = 1125
2. 应用程序标识 = *LOCAL.DB2_01.081220102511
3. 序号 = 00001
4. 应用程序名 = db2bp.exe
5. CONNECT 授权标识 = DB2ADMIN
6. 应用程序状态 = 正在执行备份
7. 状态更改时间 = 2008-12-20 18:24:55.783743
8. 应用程序代码页 = 1208
9. 挂起的锁定 = 18
10. 总计等待时间(毫秒) = 0
11. 锁定列表
12. 锁定名称 = 0x0400000000000000000000006F-注:表空间ID
13. 锁定属性 = 0x00000000
14. 发行版标志 = 0x40000000
15. 锁定计数 = 1
16. 挂起计数 = 0
17. 锁定对象名 = 4
18. 对象类型 = 内部联机备份锁定
19. 表空间名 = SYSTOOLSPACE
20. 方式 = X
21. 锁定名称 = 0x0700000000000000000000005A
22. 锁定属性 = 0x00000000
23. 发行版标志 = 0x40000000
24. 锁定计数 = 1
25. 挂起计数 = 0
26. 锁定对象名 = 7
27. 对象类型 = 自动调整大小锁定
28. 方式 = S
29. 锁定名称 = 0x0000000000000000000000007A
30. 锁定属性 = 0x00000000
31. 发行版标志 = 0x40000000
32. 锁定计数 = 1
33. 挂起计数 = 0
34. 锁定对象名 = 0
35. 对象类型 = 自动存储器锁定
36. 方式 = S
37. 锁定名称 = 0x00000000000000000000000070
38. 锁定属性 = 0x00000000
39. 发行版标志 = 0x40000000
40. 锁定计数 = 1
41. 挂起计数 = 0
42. 锁定对象名 = 0
43. 对象类型 = 表空间
44. 表空间名 = SYSCATSPACE
45. 方式 = IN
上述我们给大家讲解了DB2的一些内部锁,其实这些内部锁是DB2内部的一种自我保护机制,它类似Oracle数据库的LATCH(但也不完全一样,因为Oracle的LATCH只对内存加锁,而DB2的内部锁有些对内存加锁,有些对数据库对象加锁)。这些内部锁在通常情况下我们可以不用关注,在这里之所以给大家讲解这么多,主要想要大家了解DB2数据库的内部结构。更重要的是,真相知道得越多,越有信心,知其然并且知其所以然。
6.5 设置锁相关的注册变量
DB2从版本8以后先后引入了三个DB2注册变量-- DB2_EVALUNCOMMITTED、DB2_SKIPDELETED和DB2_SKIPINSERTED来提高并发。
为什么要引入这三个变量呢?在DB2没有这三个变量前,如果一个用户正在更改(UPDATE)、插入(INSERT)或删除(DELETE)一行,那么DB2会在这一行加上排它锁(EXCLUSIVE),别的用户不能读写,除非使用UR隔离级别。其实目前市场上除了Oracle外,Informix、SQL Server和Sybase等数据库对锁的控制采用的都是这种方式。而Oracle数据库有回滚段(ROLLBACK SEGMENT),在Oracle数据库中对于INSERT操作,回滚段记录插入记录的ROWID;对于UPDATE操作,回滚段记录更新字段的旧值(BEFORE IMAGE);对于DELETE操作,回滚段记录整行的数据。由于Oracle有了回滚段,可以实现多版本读,所以在用Oracle进行数据库开发时,很少关注锁的情况,因为大部分情况下应用都是可以读的,只不过有的时候大不了读以前的"BEFORE IMAGE"罢了。所以很多使用Oracle进行开发的用户在转向DB2开发时,都特别郁闷。而DB2为了改善应用程序并发性,从DB2 V8以后就陆续引入了这三个变量。这三个变量也是DB2客户提出要求IBM改进的,这种需求最初是由SAP提出的。这三个变量并不会改变锁的本质,只不过是了解它们的工作方式和机制可以使我们根据我们的业务逻辑来合理地设置调整以提高应用程序并发。
下面我们先通过一个例子来说明没有这三个变量之前的一些锁的情况。假设T1表中有5条记录,分别为11、22、33、44、55。其中第2条记录22被删除了,现在有一个Session 1要重新插入一条新的记录22;同时第二个Session 2执行了"db2 select * from t1 where id >11 and id<44",正常的话它应该检索到33这条记录,但是由于现在插入的记录22也包含在这个谓词限定范围内,所以这个时候Session 2处于LOCKWAIT状态,输出结果如下:
1. Session 1 Session 2
2. db2 CONNECT TO SAMPLE db2 +c INSERT INTO t1 VALUES(22)
3. db2 CONNECT TO SAMPLE db2 SELECT * FROM t1 WHERE id >11 and id <44