1、概念描述程序员
所谓的锁升级(lock escalation),是数据库的一种做用机制,为了节约内存的开销, 其会将为数众多并占用大量资源的细粒度的锁转化为数量较少的且占用相对较少资源的粗粒度的锁,多数状况下主要指将为数众多的行锁升级为一个表锁。 固然,DB2 支持不少粒度的锁,如表空间(table space),表(table),行(row)以及索引(index)等。sql
通常涉及到锁升级优化的参数调整,涉及的参数有如下几个:数据库
LOCKTIMEOUT并发
LOCKLISTapp
MAXLOCKSide
下面咱们大概了解一下这几个参数的定义:工具
LOCKTIMEOUT(锁定超时)----此参数指定应用程序为获取一个锁定将等待的秒数,以帮助避免应用程序出现全局死锁。默认值-1 [-1; 0 - 32 767 ]性能
LOCKLIST----此参数指示分配给锁定列表的内存量。每一个数据库都有一个锁定列表,锁列表包含了并发链接到该数据库的全部应用程序所持有的锁。锁定是数据库管理器用来控制多个应用程序并发访问数据库中数据的机制。行和表均可以被锁定。默认值automatic [4 – 524288]优化
MAXLOCKS----此参数定义应用程序挂起的锁定列表的百分比,必须在数据库管理器执行锁定升级以前填写该列表。当任何一个应用程序持有的锁数量达到这个百分比时,会选取“行锁最多”的表进行锁升级。默认值 automatic [1 – 100]ui
经过如下方法,可查看每一个数据库对于锁的相应配置
$ db2 get db cfg for <dbname>|grep -i lock
Max storage for lock list (4KB) (LOCKLIST) = 4096
Percent. of lock lists per application (MAXLOCKS) = 10
Interval for checking deadlock (ms) (DLCHKTIME) = 10000
Lock timeout (sec) (LOCKTIMEOUT) = -1
Block log on disk full (BLK_LOG_DSK_FUL) = NO
Block non logged operations (BLOCKNONLOGGED) = NO
Lock timeout events (MON_LOCKTIMEOUT) = NONE
Deadlock events (MON_DEADLOCK) = WITHOUT_HIST
Lock wait events (MON_LOCKWAIT) = NONE
Lock wait event threshold (MON_LW_THRESH) = 5000000
Lock event notification level (MON_LCK_MSG_LVL) = 1
2、锁升级的产生及影响
何时会发生锁升级呢?
其实对每个锁,DB2 数据库都要耗费必定的内存资源来管理并维护(通常状况下,对某个对象上所加的第一个锁须要 256 字节,而在该对象上所加的锁从第二个锁开始就只须要 128 字节了)。 所以,若是在一个表上,有大量的行锁被请求时,为了节约数据库资源的占用,“聪明的”数据库管家会用一个锁住整个表的表锁来代替为数众多的行锁,从而释放了本来大量行锁所占用的资源。 而这个过程,就被称之为锁升级。那么,数据库何时会将行锁自动升级为表锁、锁升级遵循怎样的规律、该如何预测锁升级的发生呢? 这里就须要提到两个影响数据库锁升级的 DB2 数据库配置参数:
DB2 数据库主要在如下两种情形时会进行锁升级:
1) 当一个应用的锁所使用的内存 >LOCKLIST × MAXLOCKS
2) 多个应用的锁使用的内存 >LOCKLIST
那若是发生锁升级了,会有什么影响?
因为这是数据库自行控制的机制,咱们在不经意之间享受到了好处的同时,也经常受到该机制的困扰。 显而易见,一旦下降了系统并发性及性能,并行改串行,性能会随之下降。性能下降可由如下几个可能的缘由产生:
不一样事务对于同一张表引起的锁升级会诱发死锁(deadlock)
在锁升级发生后,因为同表的并发请求被强制转换成串行处理,若是锁等待的时间不是足够长的话,会被数据库“误判”为 lock waiting timeout,从而误导程序员判断问题的根本缘由。此时,经常会被认为是因为死锁引发的锁等待时间过长
当一个应用程序使用的LOCKLIST的百分比达到MAXLOCKS时,数据库管理器将执行一次锁升级(lock escalation),在这个操做中将使行锁转换成单独的一个表锁。并且,若是LOCKLIST快要耗尽,数据库管理器将找出持有一个表上最多行锁的链接,并将这些行锁转换成表锁,以释放LOCKLIST内存。锁定整个表会大大下降并发性,死锁的概率也就增长了。
锁升级的问题,因为通常不在应用程序日志里面进行记录,因此很难被捕获到。幸运的是DB2提供了多种类型的日志以及一些数据库工具来确认和定位相似问题的发生。
如下将介绍几种关于数据库锁升级问题的探知方法供各位参考。有的是基于事件捕获型的,有的是基于统计的,可经过自行判断用哪一种方式适合本身:
3、锁监控部署
日志路径(默认):$/db2home/db2inst1/sqllib/db2dump/db2inst1.nfy
预置条件:须要预先打开snapshot的 lock monitor;在 DB2 v9.7 以后,能够 set mon_lck_msg_lvl = 1(咱们下面的示例版本是DB2 v9.1)
下面是该通知日志的一个锁升级log示例
2017-03-18-01.34.29.630201 Instance:db2inst1 Node:001
PID:28236(db2agntp (BASSDB) 1) TID:1 Appid:172.16.5.54.54061.170317172607
data management sqldEscalateLocks Probe:2
ADM5500W DB2 is performing lock escalation. The total number of locks
currently held is “57630”, and the target number of locks to hold is “28815”.
日志路径(默认):$/db2home/db2inst1/sqllib/db2dump/db2diag.log
预置条件:须要预先打开snapshot的 lock monitor;在 v9.7 以后,能够 set mon_lck_msg_lvl = 1(咱们下面的示例版本是DB2 v9.1)
下面是该诊断日志的一个锁升级log示例
2017-03-18-01.34.29.667386+480 E10178829A480 LEVEL: Warning
PID : 28236(db2agntp (BASSDB) 1) TID : 1 PROC: db2agntp (BASSDB) 1
Instance : db2inst1 Node :001
APPHDL : 0-22-1 Appid:172.16.5.54.54061.170317172607
AUTHID : BASS2
FUNCTION : DB2 UDB, data management sqldEscalateLocks, probe:3
MESSAGE :ADM5502W The escalation of “57624” locks on table “BASS2.DWD_CUST_RELATION_20170317” to lock intent “X” was successful.
DB2 数据库快照能够用来采集一段时间范围内数据库活动的一些统计信息以及某个时间点数据库的状态信息等
打开监视器:db2 -v update monitor switches using lock on
启用监视器:db2 -v commit / db2 -v terminate
收集快照:db2 -v get snapshot for database on bassdb | grep -i lock
下面是输出样例
Locks held currently = 2541
Lock waits = 38884
Time database waited on locks (ms) = 659308372
Lock list memory in use (Bytes) = 648832
Deadlocks detected = 110
Lock escalations = 0
Exclusive lock escalations = 0
Agents currently waiting on locks = 0
Lock Timeouts = 159
Internal rollbacks due to deadlock = 327
Memory Pool Type = Lock Manager Heap
4、锁升级优化
锁升级问题发生后如何经过参数设置来优化?
1. 锁升级问题能够经过增长LOCKLIST和MAXLOCKS数据库参数的大小来解决。可是,若是仍然遇到锁定问题,应检查是否因未能提交事务而未释放已更新行上的锁。
2. LOCKLIST配置参数的计算方法以下(操做系统为64位平台):
(1) 计算锁列表大小的下限:(512 * 32 * MAXAPPLS)/4096。其中,512是每一个应用程序平均所含锁数量的估计值,32是对象(已有一把锁)上每把锁所需的字节数。(在 32 位平台上须要 40 位,那么 64 位平台上须要 64 位)。
(2) 计算锁列表大小的上限:(512 * 128 * MAXAPPLS)/4096。其中,128是某个对象上第一把锁所需的字节数。(在 32 位平台上须要 80 位,在 64 位平台上须要 128 位)。
(3)对于您的数据,估计可能具备的并发数,并根据您的预计为锁列表选择一个初始值,该值位于您计算出的上限和下限之间。
3. 若是在某个可行方案中将 MAXAPPLS设置为 AUTOMATIC,那么也应该将 LOCKLIST设置为 AUTOMATIC。
4. MAXLOCKS配置参数的计算方法以下:
MAXLOCKS = 100 * (512锁/应用程序 * 32字节/锁 *2)/(LOCKLIST * 4096字节)
该公式容许任何应用程序持有的锁是平均数的两倍。若是只有几个应用程序并发地运行,则能够增大MAXLOCKS,由于在这些条件下锁列表空间中不会有太多争用。
5. maxlocks * locklist * 4096 /(100 * 64)(在除HP-UX 环境的 64 位系统上)
4096 是一页中的字节数,100 是容许 maxlocks 具备的最大百分比值,64 是每一个锁定的字节数。假设肯定其中一个应用程序须要 1000 个锁定,而且您不但愿发生锁定升级,那么应为该公式中的 maxlocks 和 locklist 选择值,以便结果大于 1000。对 maxlocks 使用 10 而且对 locklist 使用 100,该公式将产生多于所需的 1000 个锁定。
6. LOCKLIST值是与 MAXLOCKS参数一块儿调整的,所以,若是禁用 LOCKLIST参数自调整功能,也将自动禁用 MAXLOCKS参数自调整功能。若是启用 LOCKLIST参数自调整功能,也将自动启用 MAXLOCKS参数自调整功能。
7. maxlocks 参数乘以 maxappls 参数不能小于 100。
8. maxlocks = 2 * 100 / maxappls(其中 2 用来完成两次平均,而 100 表示容许的最大百分比值。)
9. maxlocks = 2 * 100 / (并发运行的应用程序的平均数目) (若是仅有几个并发运行的应用程序,可用此公式代替。)
锁升级致使异常问题确认后,如何解决 ?
参照前文所述致使发生锁升级的发生条件中的描述,显而易见咱们有以下的方式来尽量的避免锁升级:
保持 MAXLOCKS 不变,加大 LOCKLIST 的值:DB2 会增长分配给锁列表的整体内存容量。这样在单个应用程序可以持有的锁列表的最大百分比不变的状况下, 任意一个应用程序在锁升级前可以持有的锁的数量都会有所增长。该配置比较适合系统中有多个应用程序都有可能持有大量行锁的场合。
保持 LOCKLIST 不变,加大 MAXLOCKS 的值:DB2 不会增长分配给锁列表的整体内存容量,但会增大单个应用程序可以持有的锁列表的最大百分比。 这样某个特定的应用程序在锁升级前可以持有的锁的数量会有所增长。该配置比较适合系统中只有少数的应用程序有可能持有大量行锁的场合。
同时加大 LOCKLIST 和 MAXLOCKS 的值:DB2 会同时增长分配给锁列表的整体内存容量和增大单个应用程序可以持有的锁列表的最大百分比。 该配置比较适合系统内存容量比较充裕的场合。
因为系统总体内存容量的限制,不可能无限增大上述参数的值(由于调优了这部分锁内存相关的参数以后势必会影响其余内存相关的设置), 因此须要在一个较为合理的范围内控制该参数的取值。篇幅所限,笔者这里就一点而过,有兴趣的读者能够自行研究。此外,适当的加大 LOCKTIMEOUT 的设值能够有效的避免锁等待而致使的超时现象。 毕竟咱们都不但愿有“Error”关键字出如今咱们的系统日志当中。固然 DB2 有本身的回滚机制,不至于会出现业务数据遭到损失的状况。
5、举例说明
下面咱们用一个具体的例子来理解:
2017-02-23-14.21.20.342532 Instance:db2inst1 Node:000
PID:253627(db2agent (BASSDB) 0) TID:1 Appid:*LOCAL.db2inst1.0600D1010730
data management sqldEscalateLocks Probe:4 Database: BASSDB
ADM5503E The escalation of "1428392" locks on table "BASS2.ODS_EXTENT_DAILY " to
lock intent "X" has failed. The SQLCODE is "-911".
红色部份内容说明了有锁升级发生,可是失败了,失败的缘由要看Reason Code ADM5503E, 这里的状况是lock timeout, 简单的解决办法是增长LOCKTIMEOUT 可是这并很差,若是内存还有剩余的话仍是增长LOCKLIST比较好一点,固然了,若是没办法增长LOCKLIST的话,那就得从程序处着手了:
本来的db参数设置为:
Max storage for lock list (4KB) (LOCKLIST) = 50000
Percent. of lock lists per application (MAXLOCKS) = 50
Lock timeout (sec) (LOCKTIMEOUT) = 60
对LOCKLIST进行了更改,直接double
Max storage for lock list (4KB) (LOCKLIST) = 100000
Percent. of lock lists per application (MAXLOCKS) = 50
Lock timeout (sec) (LOCKTIMEOUT) = 60
如今没有了锁升级的报警了。
对于本来的配置能够算出这些内存空间最多能够锁住多少行的数据:
50000*4K*50%*1024=102400000 Byte
咱们再比对一下官方的文档(咱们的系统为64位):
On 32-bit platforms, each lock requires 36 or 72 bytes of the lock list,
depending on whether other locks are held on the object:
On 64-bit platforms, each lock requires 56 or 112 bytes of the lock list,
depending on whether other locks are held on the object:
对于咱们客户的具体状况,最多能够锁住 102400000/56=1828571 一百多万行,咱们经过这个计算结果与db2diag.log中的sql语句须要的锁相比较,没有超过这个值的锁升级成功,而在咱们改过参数后——
100000*4K*50%*1024=204800000 Byte
204800000/56=3657142
发现有三百多万行锁升级成功的记录:
2017-02-23-14.35.21.435254 Instance:db2inst1 Node:000
PID:323542(db2agent (BASSDB) 0) TID:1 Appid:*LOCAL.db2inst1.070D92045332
data management sqldEscalateLocks Probe:3 Database:BASSDB
ADM5502 The escalation of "3124245" locks on table "BASS2.ODS_OTHER_DAY" to
lock intent "X" was successful. 其实关于锁的内容相对较多,理解起来也相对复杂,建议多从实际演练中增长锁升级的优化经验,多配置一些监控工具以便对数据的分析。