首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 数据库 > 其他数据库 >

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

2012-08-15 
关于DB2 数据库并发性(1)关于 DB2 数据库并发性的探讨(转)??简介:?OLTP 数据库通常是高并发的使用模式,具

关于DB2 数据库并发性(1)
关于 DB2 数据库并发性的探讨(转)?

?

简介:?OLTP 数据库通常是高并发的使用模式,具有高的并发性对 OLTP 系统来说至关重要,并发事务实际上取决于资源的使用状况,原则上应尽量减少对资源的锁定时间,减少对资源的锁定范围,从而能够尽量增加并发事务的数量,那么影响并发的因素有哪些呢?这篇系列文章分 2 部分就这个内容进行一些探讨,同时会尽量使用不同的工具(数据库快照、事件监视器、控制中心等)来演示如何监控资源的使用状况,让大家在获得知识的同时,也可以熟悉各种工具的使用。

?


设置 DB2 注册表变量 DB2OPTIONS,将自动提交功能关闭,后面的实验默认都基于这个设置。

A 列上建有主键10 行TEST_NO_PRI关于DB2 数据库并发性(一)无主键和
UNIQUE INDEX 同样的 10 行数据

使用 RR 隔离级别对 TEST_HAVE_PRI 表进行查询,见图 2:


图 2. 执行查询的 session1 窗口
关于DB2 数据库并发性(一)

可以看到表 TEST_HAVE_PRI 上加了 IS 锁,行上加了 S 锁,这是通过主键查询时锁的使用情况,见图 3:


图 3. 快照监控窗口
关于DB2 数据库并发性(一)

通过主键字段查询后,被 RR 隔离级别锁定的行,不能够被其他事务更新(UPDATE/DELETE),session2 中的 UPDATE 事务将被锁住,见图 4:


图 4. 执行 UPDATE 事务的 session2 窗口
关于DB2 数据库并发性(一)

通过 DB2 控制中心工具可以查看锁定链条——即谁锁定了谁,打开‘应用程序’窗口,可以看到选中的应用程序状态为‘正在等待锁定’——就是 session2 中的事务的状态,见图 5:


图 5. 控制中心应用程序界面
关于DB2 数据库并发性(一)

点击‘显示锁定链’按钮,进入下一窗口,下图如何解释呢?


图 6. 应用程序锁定链界面
关于DB2 数据库并发性(一)

请点击‘图注’按钮,先了解一下各种图形元素所代表的意思,见图 7:


图 7. 图注界面
关于DB2 数据库并发性(一)

根据图 7 所示,图 6 所表示的意思为:下方框事务正在等待上方框事务释放锁定,也就是 session2 中的事务正在等待 session1 中的事务释放锁定。

更详细的信息,可以通过右键点击方框,选择‘显示锁定详细信息’菜单,见图 8:


图 8. 应用程序锁定链界面
关于DB2 数据库并发性(一)

下图为 session1 中的查询事务获得的锁的详细信息——获得一个表 IS 锁、一个行 S 锁,见图 9:


图 9. 锁定详细信息界面
关于DB2 数据库并发性(一)

图 10 为 session2 中的更新事务获得的锁的详细信息,这个事务处于正在等待锁定 状态,获得了一个表 IX 锁,同时想要获得行 X 锁,但是不成功,于是发生了锁等待,与句柄为 901 的代理程序(即运行查询事务的 session1 应用)中的行 S 锁冲突(见图 9),所以只能等待,直到句柄为 901 的代理程序提交或者回滚工作单元,才能真正获得行 X 锁。


图 10. 锁定详细信息界面
关于DB2 数据库并发性(一)

表中其他的行可以被其他事务更新,见图 11:


图 11. 执行 UPDATE 操作的 session3 窗口
关于DB2 数据库并发性(一)

使用 RR 隔离级别对 TEST_NO_PRI 表进行查询,让我们看看不通过主键字段查询时锁的使用情况,见图 12:


图 12. 执行查询的 session1 窗口
关于DB2 数据库并发性(一)

可以看到表 TEST_NO_PRI 上直接加了 S 锁,其他任何事务不能对此表做任何更新操作 (DELETE/UPDATE/INSERT),见图 13:


图 13. 快照监控窗口
关于DB2 数据库并发性(一)
A 列上建有索引

使用 RS 隔离级别对 TEST 表查询,见图 14:


图 14. 执行查询的 session1 窗口
关于DB2 数据库并发性(一)

可以看到表 TEST 上加了 IS 锁,行上加了 NS 锁(存在 32768 个行 NS 锁,这里不一一列举),见图 15:


图 15. 快照监控窗口
关于DB2 数据库并发性(一)

预更新(UPDATE/DELETE)这些行的事务将被锁住,见图 16、图 17:


图 16. 执行 UPDATE 事务的 session2 窗口
关于DB2 数据库并发性(一)

图 17. 执行 DELETE 事务的 session3 窗口
关于DB2 数据库并发性(一)

但是可以插入任何行,见图 18:


图 18. 执行 INSERT 事务的 session4 窗口
关于DB2 数据库并发性(一)

可以看到表 TEST_HAVA_PRI 上加了 IX 锁,被取的当前行 (a= ’ 1 ’ ) 加了 U 锁,符合条件的其他行暂时不加锁,见图 20:


图 20. 快照监控窗口
关于DB2 数据库并发性(一)

这时其他事务不可以对这行进行更新操作 (UPDATE/DELETE) ——因为行上的 U 锁与 X 锁排斥,但是可以对其他行进行更新(比如对 a= ’ 3 ’这行),见图 21、图 22:


图 21. 执行 UPDATE 事务的 session2 窗口
关于DB2 数据库并发性(一)

图 22. 执行 UPDATE 事务的 session3 窗口
关于DB2 数据库并发性(一)

转到 session1 窗口,继续 FETCH 下一行,这时将释放前一行 (a= ’ 1 ’ ) 上的 U 锁,在当前行上 (a= ’ 2 ’ ) 加 U 锁,见图 23:


图 23. FETCH 下一行
关于DB2 数据库并发性(一)

转到 session2 窗口,可以看到之前被锁住的 UPDATE 语句已经执行成功,见图 24:


图 24. 执行 UPDATE 事务的 session2 窗口
关于DB2 数据库并发性(一)

在 session4 窗口对 a= ’ 2 ’这行进行 UPDATE 操作,可以预见将被锁住,见图 25:


图 25. 执行 UPDATE 事务的 session4 窗口
关于DB2 数据库并发性(一)

转到 session1 窗口,继续 FETCH 下一行,将释放 a= ’ 2 ’这行上的 U 锁(session4 中对 a= ’ 2 ’这行的 UPDATE 事务将成功执行,为什么呢?),将要在 FETCH 的下一行上加 U 锁,发现 session1 中的游标 FETCH 操作被锁住,这又为什么呢?

因为 session1 中的事务释放了 a= ’ 2 ’上的 U 锁,所以 session4 中被锁住的针对 a= ’ 2 ’这条记录进行的 UPDATE 事务能够执行成功,见图 26:


图 26. 执行 UPDATE 事务的 session4 窗口
关于DB2 数据库并发性(一)

为什么 session1 中的事务(FETCH 下一条记录)被锁住呢?是因为这次恰好要 FETCH a= ’ 3 ’这条记录,需要在行上加 U 锁,这与 session3 中的 update 事务对这行所加的 X 锁相排斥,所以 session1 窗口的 FETCH 下一行操作发生了锁等待,直到 session3 中的事务提交或者回滚为止,见图 27:


图 27. 执行 FETCH 操作的 session1 窗口
关于DB2 数据库并发性(一)
没有索引

采用 UR 隔离级别查询 TEST_NO_IND 表,同时运行快照,可见,使用 UR 隔离级别查询时,对表 TEST_NO_IND 加了 IN 锁,IN 就是 intent none 意思,就是不加任何锁,见图 28、图 29:


图 28. 执行查询事务的 session1 窗口
关于DB2 数据库并发性(一)

图 29. 快照监控窗口
关于DB2 数据库并发性(一)

综上所述,离级别对并发性具有最显著的影响,不同隔离级别获得的资源的锁定范围也不同,如果所有事务都能做到不过分贪婪的占有锁资源——锁的范围大、占用时间长,那么事务之间发生锁冲突的可能性将大大降低,事务的并发性也将会很好。那么如何选择正确的隔离级别呢?

使用的隔离级别不仅影响数据库对并发性的支持如何,而且影响并发应用程序的性能。通常,使用的隔离级别越严格,并发性就越小,某些应用程序的性能可能会越低,因为它们要等待资源上的锁被释放。那么,如何决定要使用哪种隔离级别呢?最好的方法是确定哪些现象是不可接受的,然后选择能够防止这些现象发生的隔离级别:

如果正在执行大型查询,而且不希望并发事务所做的修改导致查询的多次运行返回不同的结果,则使用可重复的读隔离级别。如果希望在应用程序之间获得一定的并发性,还希望限定的行在事务执行期间保持稳定,则使用读稳定性隔离级别。如果希望获得最大的并发性,同时不希望查询看到未提交的数据,则使用游标稳定性隔离级别。如果正在只读的表 / 视图 / 数据库上执行查询,或者并不介意查询是否返回未提交的数据,则使用未提交的读隔离级别。 对于统计类报表,不需要得到十分精确的数据,那么最好使用 UR 隔离级别,既可以节省昂贵的锁列表资源,也不会因为锁冲突影响其他事务的执行,同时也不会受到其他事务的影响,顺利的得到统计结果。未提交的读隔离级别通常用于那些访问只读表和视图的事务,以及某些执行 SELECT 语句的事务(只要其他事务的未提交数据对这些语句没有负面效果)。 顾名思义,其他事务对行所做的更改在已经提交之前对于使用未提交的读隔离级别的事务是可见的。但是,此类事务不能看见或访问其他事务所创建的表、视图或索引,直到那些事务被提交为止。类似地,如果其他事务删除了现有的表、视图或索引,那么仅当进行删除操作的事务终止时,使用未提交的读隔离级别的事务才能知道这些对象不再存在了。(一定要注意一点:当运行在未提交的读隔离级别下的事务使用可更新游标时,该事务的行为和在游标稳定性隔离级别下运行一样,并应用游标稳定性隔离级别的约束。)

数据库锁参数与并发性

当应用程序挂起的锁定总数达到可供应用程序使用的最大锁定列表空间量时,锁定将会升级,将影响到应用程序并发性。可用锁定列表空间量由 locklist 和 maxlocks 配置参数确定。

当应用程序达到允许的最大锁定数并且没有其他要升级的锁定时,它将使用锁定列表中为其他应用程序分配的空间。当整个锁定列表已满时,将发生错误。

以下几种原因可能会导致产生过量锁定升级:

锁定列表大小(locklist)对于并行应用程序数目而言可能太小可供每个应用程序使用的锁定列表百分比(maxlocks)可能太小一个或多个应用程序使用的锁定数可能过量。

要解决这些问题,可以:

增加 locklist 配置参数值。增加 maxlocks 配置参数值。标识具有大量锁定(请参阅locks_held_top监视器元素)的应用程序,或借助以下公式并将该值与maxlocks进行比较以标识在锁定列表中挂起过量锁定的应用程序:
(((locks held * 36) ? (locklist * 4096)) * 100)

这些应用程序还可能因为在锁定列表中使用过量资源而导致其他应用程序中发生锁定升级。这些应用程序可能需要求助于使用表锁定(而不是行锁定),尽管表锁定可能导致lock_waitslock_wait_time增加。

锁参数(LOCKLIST、MAXLOCKS 和 LOCKTIMEOUT)背景知识

这些与锁相关的控制都是数据库配置参数:

LOCKLIST表明分配给锁列表的存储容量。每个数据库都有一个锁列表,锁列表包含了并发连接到该数据库的所有应用程序所持有的锁。锁定是数据库管理器用来控制多个应用程序并发访问数据库中数据的机制。行和表都可以被锁定。根据对象是否还持有其它锁,每把锁需要的锁列表字节数不一样:

在 32 位平台上,每个锁需要 48 或 96 字节的锁定列表,这取决于是否对该对象挂起了其他锁:

? 对于没有挂起其他锁的对象,挂起一个锁需要96字节对于已经挂起了锁的对象,记录一个锁需要48字节

64位平台(HP-UX/PA-RISC除外)上,每个锁需要64128字节的锁定列表,这取决于在该对象上是否挂起了其他锁定:

? 对于没有挂起其他锁定的对象,挂起一个锁定需要128字节对于存在一个挂起的锁定的对象,记录一个锁定需要64字节。MAXLOCKS定义了应用程序持有的锁列表的百分比,在数据库管理器执行锁升级之前必须填充该锁列表。当一个应用程序所使用的锁列表百分比达到MAXLOCKS时,数据库管理器会升级这些锁,这意味着用表锁代替行锁,从而减少列表中锁的数量。当任何一个应用程序所持有的锁数量达到整个锁列表大小的这个百分比时,对该应用程序所持有的锁进行锁升级。如果锁列表用完了空间,那么也会发生锁升级。数据库管理器通过查看应用程序的锁列表并查找行锁最多的表,来决定对哪些锁进行升级。如果用一个表锁替换这些行锁,将不再会超出MAXLOCKS值,那么锁升级就会停止。否则,锁升级就会一直进行,直到所持有的锁列表百分比低于MAXLOCKSMAXLOCKS参数乘以MAXAPPLS参数不能小于100

虽然升级过程本身并不用花很多时间,但是锁定整个表(相对于锁定个别行)降低了并发性,而且数据库的整体性能可能会由于对受锁升级影响的表的后续访问而降低。

下面是一些控制锁列表大小的建议:

? 经常进行提交以释放锁。当执行大量更新时,更新之前,在整个事务期间锁定整个表(使用SQL LOCK TABLE语句)。这只使用了一把锁从而防止其它事务妨碍这些更新,但是对于其他用户它的确减少了数据并发性。使用alter TABLE语句的LOCKSIZE参数控制如何在持久基础上对某个特定表进行锁定。查看应用程序使用的隔离级别。使用可重复读隔离级别在某些情况下可能会导致自动执行表锁定。当有可能减少所持有共享锁的数量时,可以使用游标稳定性(Cursor Stability)隔离级别。如果没有损害应用程序完整性需求,那么可以使用未提交的读隔离级别而不是游标稳定性隔离级别,以进一步减少锁的数量。

使用下列步骤确定数据库锁列表所需的页数:

    计算锁列表大小的下限:(512 * 32 * MAXAPPLS) / 4096,其中512是每个应用程序平均所含锁数量的估计值,32是对象(已有一把锁)上每把锁所需的字节数。计算锁列表大小的上限:(512 * 64 * MAXAPPLS) / 4096,其中64是某个对象上第一把锁所需的字节数。对于您的数据,估计可能具有的并发数,并根据您的预计为锁列表选择一个初始值,该值位于您计算出的上限和下限之间。

使用数据库系统监视器调优MAXLOCKS值。

设置MAXLOCKS时,请考虑锁列表的大小(LOCKLIST):

MAXLOCKS = 100 * (512锁/应用程序* 32字节/锁* 2) / (LOCKLIST * 4096字节)

该样本公式允许任何应用程序持有的锁是平均数的两倍。如果只有几个应用程序并发地运行,则可以增大MAXLOCKS,因为在这些条件下锁列表空间中不会有太多争用。

    LOCKTIMEOUT指定了应用程序为获取锁所等待的秒数。这有助于应用程序避免全局死锁。 如果将该参数设置成0,那么应用程序将不等待获取锁。在这种情形中,如果请求时没有可用的锁,那么应用程序立刻会接收到-911。如果将该参数设置成-1,那么将关闭锁超时检测。在这种情形中,应用程序将等待获取锁(如果请求时没有可用的锁),一直到被授予了锁或出现死锁为止。

如何更改这些参数

要更改锁参数,请运行以下命令:

db2 -v update db cfg for DB_NAME using LOCKLIST a_number db2 -v update db cfg for DB_NAME using MAXLOCKS b_number db2 -v update db cfg for DB_NAME using LOCKTIMEOUT c_number db2 -v terminate

监控步骤

一旦锁列表满了,由于锁升级生成更多的表锁和更少的行锁,因此减少了数据库中共享对象的并发性,从而降低了性能。另外,应用程序间可能会发生更多死锁(因为它们都等待数量有限的表锁),这会导致事务被回滚。当数据库的锁请求达到最大值时,应用程序将接收到值为-912SQLCODE。如果锁升级造成并发性方面的问题,则可能需要增大LOCKLIST参数或MAXLOCKS参数的值。可以使用数据库系统监视器来确定是否发生锁升级,跟踪应用程序(连接)遭遇锁超时的次数,或者数据库检测到的所有已连接应用程序的超时情形。

    首先,运行下面这个命令以打开针对锁的DB2监视器:
db2 -v update monitor switches using lock on db2 -v terminate

    然后收集数据库快照:
db2 -v get snapshot for database on DB_NAME

    在快照输出中,检查下列各项:
Locks held currently = 0 Lock waits = 0 Time database waited on locks (ms) = 0 Lock list memory in use (Bytes) = 504 Deadlocks detected = 0 Lock escalations = 0 Exclusive lock escalations = 0 Agents currently waiting on locks = 0 Lock Timeouts = 0

如果“Lock list memory in use (Bytes) ”超过定义的LOCKLIST大小的50%,那么就增加LOCKLIST数据库配置参数中的4KB页的数量。锁升级、锁超时和死锁将表明系统或应用程序中存在某些潜在问题。锁定问题通常表明应用程序中存在一些相当严重的并发性问题,在增大锁列表参数的值之前应当解决这些问题。

避免死锁

从事务双方的锁定关系上来讲有死锁和活锁 2 种,所谓活锁:举例来说可能会存在事务 A 在等待事务 B 释放所占用的锁资源,但是事务 B 并不等待事务 A 释放所占用的锁资源,所谓死锁:举例来说可能会存在事务 A 在等待事务 B 释放所占用的锁资源,同时事务 B 在等待事务 A 释放所占用的锁资源,也就是说,事务之间互相等待,如果没有合理设置检查死锁的时间间隔 (DLCHKTIME) 参数,可能会导致相关事务双方永远等待下去,DLCHKTIME 参数默认设置为 10000 毫秒——即 10 秒,也就是说每隔 10 秒钟,死锁检测进程会自动检查数据库范围内有无死锁存在,如果发现了死锁存在,那么将随机挑选一个事务并强制终止它,这个事务将被回滚,那么这时另外一个事务将可以顺利执行下去。

总的来说,死锁可能是由下列情况导致的:

数据库发生锁定升级在系统生成的行锁定已足够的情况下应用程序显式锁定了表绑定时应用程序使用了不适当的隔离级别目录表已被锁定以供可重复读应用程序正以不同的顺序获取相同的锁定,从而导致死锁。

热点排行