DB2 锁和并发

发布时间:2016/1/6 14:37:43 浏览量:950



--学习资料整理


主要内容:
锁等待
锁升级
死锁


一、锁等待


1、锁等待:当应用程序对一个对象请求的锁不能够立刻得到时,该程序将进入一个等待服务的队列,等待占用该锁的应用程序提交或回滚来释放该锁。


2、锁定超时检测,能够防止应用程序在异常情况下无限制地等待释放锁定。


3、locktimeout参数配置任何应用程序应等待获取锁定的最长时间,这样可以帮助避免全局死锁的情况发生。如果锁定请求处于暂挂的时间大于locktimeout值,那么请求应用程序将收到错误(-911)并将其事务回滚。
locktimeout的默认值为-1,它关闭锁定超时检测。如果该值为-1,则应用程序将会出现无穷锁等待现象。


4、对于生产系统中的OLAP,一开始为60秒比较好,对于OLTP大约为10秒比较好。对于开发环境,应该使用-1,以识别和解决锁等待的情况。如果有大量的并发用户,则可能需要增加OLTP时间,以避免回滚。


5、如果快照监控结果输出中“Lock Timeouts”是一个较高的数,那么可能原因有:
a、LockTimeOut的值太低
b、某个事物持有锁的时间有所延长
c、锁升级


6、锁等待可能造成如下结果:
a、引起死锁(死锁是锁等待的一个特例),有死锁检测器处理;
b、等待超时,等待的应用程序返回SQLCODE -911和原因码68,并自动回滚;
c、如果locktimeout的值被设置为-1,则应用程序将永远等待,直到能够获得所需要的锁;
d、对于行、表、数据分区和MDC块锁定,应用程序可根据业务逻辑需要使用SET CURRENT LOCK TIMEOUT 来覆盖数据库级别的locktimeout设置。


7、锁等待问题解决流程和步骤:

a、把数据库监控开关打开:

db2 update dbm cfg using DFT_MON_LOCK on DFT_MON_STMT on
db2 update montitor switches using lock on sort on bufferpool on uow on table on statement on

补充:
查看数据库的参数配置(前提:连接上数据库):

db2 get db cfg


b、监控SQL脚本:

select agent_id, substr(stmt_text, 1, 100) as statement, stmt_elapsed_time_ms from table(snapshot_statement('SAMPLE', -1)) as b 
where agent_id in (select agent_id_holding_lk from table(snapshot_lockwait('SAMPLE', -1)) as a order by lock_wait_start_time asc fetch first 20 rows only ) order by stmt_elapsed_time_ms desc
;--(这个脚本非常使用,要熟记)

此语句会按照执行时间的长短的先后顺序排列出所有造成lockwait的SQL语句。

c、可以将上述的a和b写到shell脚本中,每隔30秒运行一次,把监控结果输出到一个文件中;

d、一旦定位引起锁等待的SQL后,若改SQL语句写的效率很低,可以考虑对该SQL调优;若该SQL没有创建最合理的索引,尝试使用db2advis工具为引起锁等待的SQL创建最合理的索引;

e、如果创建索引和调优SQL仍然不能解决问题,考虑能否根据业务逻辑选择UR隔离级别。但这种方式只能解决读的问题;

f、在业务逻辑允许的情况下,考虑设置DB2_EVALUNCOMMITTED、DB2_SKIPDELETED和DB2_SKIPINSERTED来提高并发;

g、最后考虑能否对引起锁等待的SQL语句关联的表做数据归档、业务分离等。


总结:以前,只能在数据库级别通过更改locktimeout数据库配置参数的值来指定锁定等待超时时间。锁定等待策略通过新的SET CURRENT LOCK TIMEOUT可以在语句级别指定(DB2 V9.5以后),此语句更改CURRENT LOCK TIMEOUT专用寄存器的值。CURRENT LOCK TIMEOUT专用寄存器指定在返回指示不能获取锁定的错误之前等待锁定的秒数。

8、捕获引起锁等待的SQL。使用db2pd监控工具找到正在等待的锁定的所有者,然后再找出相应的SQL语句进行调整。


a、判断数据库中是否存在大量锁等待,使用get snapshot for database来监控

db2 get snapshot for database on prod | grep -i lock

此语句执行结果中,查看Lock Waits等数据,判断数据库中是否有大量的锁等待。

b、确定哪个事务包含锁定。通过运行具有-locks属性的db2pd命令来显示有关当前锁定的信息;在Sts(状态)列中查找带“W”(表示等待){“G”,是granted,被授权获得锁}的行。“所有者”列指示哪个事务持有该锁定。

c、通过运行db2pd -transactions来标识正在运行事务的应用程序,该事务正在持有锁定;

d、通过运行db2pd -agents来标识正在运行应用程序的代理进程;

e、通过运行db2 get snapshot for application agentid 1602(对应APPHANDL)--应用程序句柄,定位锁等待的SQL语句和正在等待的agentid;


9、利用db2pd捕获锁超时;

为了捕捉死锁,可以创建一个死锁事件监视器。每当出现死锁时,此死锁事件监视器便写一个条目。但是,对于锁超时就没有类似的事件监视器。下面将举例说明,如何利用db2pd捕获锁超时。

a、锁监视db2pd选项:locks - TranHdl - database - file - showlocks - wait
其中:TranHdl:用于指定事务句柄,以便只监视由特定事务持有的锁;
showlocks:用于将锁名称扩展成有意义的解释。对于一个行锁,将显示如下信息:表空间ID、表ID、分区ID、页和槽。通过编目视图SYSCAT.TABLES,可以将表空间ID和表ID映射到相应的表名。
调用命令:
检查锁信息:db2pd -db database_name -locks 
检查锁等待:db2pd -db database_name -locks wait showlocks
检查授权锁:db2pd -db database_name -locks granted showlocks

b、DB2 V9之前版本,捕捉锁超时的唯一方法是连续的db2pd或快照监视(对于db2pd,-file和-repeat选项可用于连续的锁监视);

c、DB2 V9之后的版本,包含一种新的机制,用于在数据库出现故障或发生事件时收集监视器数据-db2cos脚本。

补:更新锁超时设置 - update db cfg for database_name using locktimeout 10

为了每当出现锁超时时启动db2cos脚本,DBA调用db2pdcfg实用程序,如下配置 
- db2pdcfg -catch locktimeout count=1,其中-catch选项指定应该自动导致调用db2cos脚本的故障或事件。
对于锁超时事件,可以指定字符串locktimeout,或者可以指定与锁超时相应的SQL错误码和原因,如db2pdcfg -cathc 911,68 count=1。
除了一些字符串值和SQL代码之外,db2pdcfg还接受内部DB2错误码。


d、db2diag.log报告中也包括错误捕捉设置。可以使用db2diag实用程序(用于检查db2diag.log内容的一个有用的实用程序)过滤db2diag.log文件,而不必在一个文本编辑器中打开它。
db2diag -g funcname:=pdErrorCatch,其中ZRC -2146435004是用于锁超时的DB2内部错误码。
可以通过如下的db2diag查看这些错误码:
db2diag -rc -2146435004

e、使用db2pdcfg清楚错误捕捉设置
db2pdcfg -catch clear




二、锁升级及调整案例


数据库管理器可以自动将锁定从行级别升级为表级别。MAXLOCKS数据库配置参数指定触发锁定升级的百分比。获取触发锁定升级的锁定的表可能不受影响。每个锁在内存中都需要一定的内存空间,为了减少锁需要的内存开销,DB2提供了锁升级的功能。锁升级是通过对表加上非意图性的表锁,同时释放行锁减少锁的数目,从而达到减少锁需要的内存开销的目的。锁升级是由数据库管理器自动完成的,数据库的配置参数锁列表页面数(LOCKLIST)和应用程序占有百分比(MAXLOCKS)直接影响锁升级的处理。

应用程序持有的锁超出允许数据库总的锁内存(locklist)百分比(maxlocks值)。假设一个表有5万条记录,某个时刻去更新2万条记录,那么该表上有20002个锁(20000个行锁,一个表级别的IX锁,一个内部锁)。每个锁都占用一定字节大小的内存,如果该表上总的锁占用的内存超过了locklist * maxlocks,那么这时DB2自动把表从行级锁升级为表级锁,经过锁升级后,该表原来有20002个锁,现在只有一个表级别的X锁。锁升级可以释放部分锁资源,但是会影响锁的并发,这这个例子中,如果不进行锁升级,其他没有更新的3万条记录还可以允许别的应用进行更改。而一旦锁升级,整个表都被加锁,这就会大大影响应用程序的并发性能。而且,如果LOCKLIST快要耗尽,数据库管理器将找出有一个表上的最多行锁的连接,并将这些行锁换成表锁,以释放LOCKLIST内存。锁整个表可以大大减少并发性,锁等待的概率也增加了。

1、监控锁升级

a、打开针对锁的DB2监视器

db2 -v update monitor swithes using lock on
db2 -v terminate

b、收集数据库快照

db2 -v get snapshot for database on database_name | grep -i lock

c、在get snapshot for locks on database_name快照输出中,查找

Lock list memory in use (Bytes) =
-- 锁占用的内存
Deadlocks detected =
-- 死锁
Lock escalctions =
-- 锁升级
Exclusive lock escalations =
-- 互斥锁升级

如果“Lock list memory in use (Bytes)”超过定义的LOCKLIST大小的50%,那么就增加LOCKLIST数据库配置参数中的4KB页的数量。
如果发生了“Lock escalations > 0” 或“Exclusive lock excalations > 0”,表示数据库中存在锁升级。锁升级会影响数据库的并发。

2、锁升级调整


a、锁升级问题可以通过增加LOCKLIST和MAXLOCKS数据库参数的大小来解决。但是,如果仍然遇到锁定的问题,很可能应该检查是否有未提交的事务,从而未释放已更新行上的锁。

b、每个数据库都有一个锁列表,该列表包含所有同时连接到数据库的应用程序所持有的锁。在32位平台上,一个对象上的第一个锁要求占48字节,而其他的锁要求占96字节。在64位平台上,第一个锁要求占128字节,而其他锁要求占64字节。
注:关于LRB(锁占用资源块 - Lock Resource Block),它在DB2的各个版本中很不一样。在DB2 V8之前,在32位平台上,在一个没有持有其他锁定的对象上持有一个锁定需要72字节,在一个持有了现存锁定的对象上记录一个锁定需要36字节;在DB2 V8之后的版本中,在一个没有持有其他锁定的对象上持有一个锁定需要64位字节,在一个持有了现存锁定的对象上记录一个锁定需要32字节。在64位平台上,要对没有其他锁定的对象上保留锁定需要112字节,要对具有现有锁定的对象上保留锁定需要56字节。

LOCKLIST表明分配给锁列表的存储容量。每个数据库都有一个锁列表,锁列表包含了并发连接到该数据库的所有应用程序所持有的锁。锁定是数据库管理器用来控制多个应用程序并发访问数据库中数据的机制。行和表都可以被锁定。

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

3、锁升级会在一下两种情况下被触发:


a、某个应用程序请求的锁所占用的内存空间超出了MAXLOCKS与LOCKLIST的乘积大小。这时,数据库管理器将试图通过为提高锁请求的应用程序申请表锁,并释放行锁来节省空间。

b、在一个数据库中已被加上的全部锁所占的内存空间超出了LOCKLIST定义的大小。这时,数据库管理器也将试图通过为提出锁请求的应用程序申请表锁,并释放行锁来节省空间。

4、锁升级是有可能失败的。
比如,现在一个应用程序已经在一个表上加有IX锁,表中的某些行上加有X锁,另一个应用程序又来请求表上的IS锁,以及很多行上的S锁,由于申请的锁数目过多引起锁升级。数据库管理器试图为该应用程序申请表上的S锁来减少锁需要的锁的数目,但S锁与表上原有的IX锁冲突,锁升级不能成功进行。如果锁升级失败,引起锁升级的应用程序将接到一个 -912的SQLCODE。

对于锁升级,大家不要以为一定是不好的。假如我现在对一个比较大的表进行大批量删除操作,那么很可能发生锁升级,但是这个时候发生锁升级是合理的。所以数据库发生锁升级时,我们一定要结合业务来判断某个表上的锁升级如何引起的,以及是否合理。如果不合理的话,我们再采取相应的步骤来增加LOCKLIST和MAXLOCKS。但是通常情况下,如果和应用程序或SQL语句相关的话,一般只调整LOCKLIST和MAXLOCKS还不能解决问题,这时应考虑去调整相应的SQL语句和为该SQL语句创建合理的索引来解决之。






三、死锁及调整案例


1、 死锁的产生是由于锁请求双方都彼此持有对方所需要的锁,这种情况又去请求锁。

死锁发生的情况举例:假定事务1在表A上获取了互斥(X)锁,而事务2在表B上获取了互斥(X)锁。现在,假定事务1尝试在表B上获取互斥(X)锁,而事务2尝试在表A上获取互斥(X)锁。这两个事务的处理都将被挂起,直到同意第二个锁请求为止。但是,因为在任何一个事务释放它目前持有的锁(通过执行或回滚操作)之前,这两个事务都不能释放它目前持有的锁(因为它们都已挂起并等待锁),所以它们都陷入了死锁循环。


2、利用事件监视器监控死锁


a、创建事件监视器

db2 create event monitor dlock for deadlocks with details write to file '$HOME/file'

db2 set event monitor dlock state 1

b、查看事件监视器具体输出:

db2evmon -db database_name -evm dlock

3、死锁案例

前提:设置locktimeout参数,不能为-1,现在设置为15,update db cfg for database_name using locktimeout 15
打开两个CLP窗口(开始->运行,db2cmd, db2)

a、第一个CLP窗口

update command options using c off-- 关闭自动提交

connect to database_name

create table deadtable(c1 varchar(20))

commit

insert into deadtable values('1')

b、第二个CLP窗口

update command options using c off-- 关闭自动提交

connect to database_name

insert into deadtable values('2')

select * from deadtable

c、在完成第二步后,等待15秒(locktimeout参数为15),在第一个窗口执行select * from deadtable

d、这时会发生死锁,因为第一个窗口和第二个窗口都在等待对方释放锁,必须让其中一个应用程序回滚,才能打破锁冲突。DB2死锁监视器进程会在10秒(DLCHKTIME的默认设置)内选择一个应用程序并使它回滚。一个窗口返回查询结果,另一个窗口将返回死锁信息。


e、总结

DB2利用一个称为死锁检测器的后台进程(db2dlock)进行死锁的检测,该进程每隔一定的时间段进行一次检测,一旦发现死锁,该进程会选择一个牺牲者。牺牲者将自动回滚,释放掉占用的锁并返回SQLCODE-911和原因码2,死锁可以消除。

注意,DB2 deadlock detetor(死锁检测器)随机选择受害者;因此,不能确定哪一个更新将发生回滚。

DLCHKTIME是设置死锁检查间隔的配置参数,该参数以毫秒为单位,其有效范围是1000至60000ms。该值过高将增加应用程序等待死锁被发现的时间,如果该值过低,死锁检测的间隔虽然加快了,但却降低了运行性能。该参数默认值为10000(10秒)。

f、引起死锁的情况及其影响程度包括:

应用程序隔离级别采用重复读和读可靠性(主要)

锁升级(主要)

锁转换(次要)

编目表的更改(中等)

参照完整性的约束(次要)


4、最小化死锁建议


当数据库发生死锁时,虽然不需要我们干预,数据库会自动处理。但是这可能影响应用程序的并发,一下是一些最小化死锁的建议:

a、在整个应用程序中,总是按相同次序访问资源可以最小化死锁。例如,如果一个应用程序组件将要访问表A,然后是表B,接着是表C;而另一个应用程序组件需要访问表A和C,那么第2个组件应该遵循先A后C的访问次序。

b、对于DB2 V9以前的版本,导致死锁的一个常见原因是锁列表数据库配置参数的大小不足,尤其是使用默认值时。因此应该合理地设置LOCKLIST和MAXLOCKS配置参数。默认情况下DB2 V9使用了STMM,它会调整锁列表大小以避免可能由此引起的锁升级和死锁。

c、确保参照完整性(Referential Integrity,RI)关系中的依赖表拥有与外键匹配的索引。

d、把DLCHKTIME设置为一个较小的值,以快速检测到死锁并回滚来打破死锁平衡。

e、死锁是一种特殊形式的锁等待,一旦我们定位引起死锁的SQL语句后,如果该SQL语句写的效率很低下,可以考虑对该SQL作出调整;如果该SQL语句上没有创建最合理的索引,可以考虑用db2advis工具对该SQL语句创建最合理的索引。

网友回复
总数:0 当前页:1 / 0 首页 上页 下页 末页
回复

品码堂 (关于我们

桂ICP备13004001号