--------------------------------------锁的分类:
(1)读锁:即共享锁(S)
(2)写锁:即排他锁(X)
S锁可以与S锁兼容,不可与X锁兼容。
X锁与S锁和X锁都不可以兼容。
举例:
一条记录加了共享锁(S),那么其他事务也可以对该条记录加共享锁(S); 但是不能加排他锁,需要等到该共享锁释放之后再加排他锁。
一条记录加了排他锁(S), 那么其他事务即不可以对该条数据加共享锁(S),也不可以加排他锁(X),需要等到该排他锁释放之后再加其他锁。
----------------------------------------锁的对象:
DB2支持对表空间、表、行和索引加锁, 行锁是DB2默认的锁对象。
不过在考虑用户应用程序的并发性的问题上,通常并不检查用于表空间和索引的锁。该类问题分析的焦点在于表锁和行锁。
---------------------------------------锁的模式:
(1)表锁模式:
强类型锁:适用于表中的所有的行,包括:S, U, X, Z
弱类型锁:又叫意向锁(intent),目的是配合行锁,在获得行锁之前必须先获得表锁,包括:IN, IS, IX, SIX
默认情况下,DB2不会实施强类型表锁,只有通过lock table锁表或放生锁升级的时候才会在表上加强类型的锁。
I: intent
N: none
S: share
X: eXclusive
U: update
Z: super eXclusive
弱类型锁模式:
IN
IS
IX
SIX
IS、IX、SIX方式用于表一级并需要行锁配合,他们可以阻止其他应用程序对该表加上排它锁。
IN锁用于表上以允许未提交读这一概念。(UR)
如果一个应用程序获得某表的IS锁,该应用程序可获得某一行上的S锁,用于只读操作,同时其他应用程序也可以读取该行,或是对表中的其他行进行更改。
如果一个应用程序获得某表的IX锁,该应用程序可获得某一行上的X锁,用于更改操作,同时其他应用程序可以读取或更改表中的其他行。
如果一个应用程序获得某表的SIX锁,该应用程序可以获得某一行上的X锁,用于更改操作,同时其他应用程序只能对表中其他行进行只读操作。
强类型锁模式:
S
U
X
Z
S、U、X和Z方式用于表一级,但并不需要行锁配合,是比较严格的表加锁策略。
如果一个应用程序得到某表的S锁。该应用程序可以读表中的任何数据。同时它允许其他应用程序获得该表上的只读请求锁。如果有应用程序需要更改读该表上的数据,必须等S锁被释放。
如果一个应用程序得到某表的U锁,该应用程序可以读表中的任何数据,并最终可以通过获得表上的X锁来得到对表中任何数据的修改权。其他应用程序只能读取该表 中的数据。U锁与S锁的区别主要在于更改的意图上。U锁的设计主要是为了避免两个应用程序在拥有S锁的情况下同时申请X锁而造成死锁的。
如果一个应用程序得到某表上的X锁,该应用程序可以读或修改表中的任何数据。其他应用程序不能对该表进行读或者更改操作。
如果一个应用程序得到某表上的Z锁,该应用程序可以读或修改表中的任何数据。其他应用程序,包括未提交读程序都不能对该表进行读或者更改操作。
notes:U锁一般用于select....for update.. 如: select * from test for update with ur
U锁是介于S和X锁之前的一种锁,目的是在对一个表可读的同时,其他app可以对其可写。(相当于使读锁和写锁可兼容)
(2)行锁模式:
S
U
X
W
NS
NX
NW
---------------------------------------DB2的隔离级别:
UR: uncommited read 不加锁
CS: cursor stability 加一行锁,在当前读到的行加S锁。
RS: read stability 在查询结果加S锁
RR: repeatable stability 读过的行都加S锁。
举例:
表staff中共有10000条数据,如下A事务执行的sql的查询结果中有10条数据:
select * from staff where name='Allen'; --10条
在该sql提交之前,B事务来更改该表的数据。
(1)A事务在查询的时候不加任何锁,则可以读到B事务未提交的数据。 ---UR
(2)在10条结果中的一条,即当前条数据加上读锁。B事务不可以更改当前条,但可以更改结果集中的其他条数据。 ---CS
(3)在所有10条结果集上都加读锁。B事务不能更改这10条结果集的数据,但是可以更改不满足sql条件的其他行数据。 ---RS
(4)将A事务读到的所有的行, 即全表,都加读锁。这样B事务就无法更改数据了。 ---RR
说明:
隔离级别只适用于读,查询的时候通知DB2管理器,决定: 不加锁/加一行锁/对结果集加锁/读过的行都加锁。
对于写操作(增删改),都需要加写锁,即排他锁(X)。
============================================================================================锁等待(lock wait)
------------------------------通过系统表,查看某个表的锁等(lock wait)
1.看某个表被什么用户/进程调用:
db2 "select * from SYSIBMADM.LOCKWAITS"|grep OPP_DETAIL_HISTORY_FACTS
找到用户:AUTHID,进程ID:AGENT_ID
此处AGENT_ID和db2 list applications中Status为"Lock-wait"的Appl一致。
2. select * from sysibmadm.snaplock;
notes: 即使有所等待的时候,SYSIBMADM.LOCKWAITS / sysibmadm.snaplock系统表里可能也会没有想过锁信息或者表的信息,因为对该表的操作可能只锁了系统表(如SYSTABLES)
------------------------------几种常见的查看锁等待(lock-wait)的方法:
------------------方法1:
找出锁等的sql语句:
监控锁等(wait locks):
db2pd -d <db_name> -locks showlocks wait -tra -app -dyn
db2pd -d sample -locks showlocks wait -tra -app -dyn -file sample_lock_wait.txt
查找步骤:
打开输出文件sample_lock_wait.txt
1.在lock模块中:
Sts为W(wait),是所等待的进程
Sts为G(Granted),表示锁被成功得到,即获得锁的进程
找到他们的TranHdl的值
2. Transactions模块
根据事务手柄TranHdl的值得到应用手柄AppHandl的值
3. Applications模块
根据AppHandl的值得到sql唯一标示代码AnchID/ StmtUID (C-AnchID C-StmtUID L-AnchID L-StmtUID中C: current,当前执行的sql语句, L: last上一句执行的语句)
4. Dynamic SQL Statements模块
根据sql唯一标示代码AnchID/StmtUID得到sql语句, NumExe表示执行次数
notes:showlocks 可选项
-----------------方法2:
1.db2pd -wlocks -db sample
得到Sts为G的即是lock holder, 为W的为waiter。找出他们对应的AppHandl(如37661,37662)
2.db2pd -apinfo 37661 -db sample
db2pd -apinfo 37662 -db sample
或者用:db2 get snapshot for application agentid 37661/37662
----------------方法3:
1.db2top -d samples -i 6
2.按U进入locks,锁请求者的进程Application Status的状态为lock wait
3.按大写L,进入lock chain,查看锁等的等待被等待信息. 110-->112, 则110为lock holder,112为lock requester。
-------------------------------找出锁等超过2分钟的表:
db2 "select rtrim(APPL_NAME),rtrim(AUTHID),rtrim(AGENT_ID),rtrim(TABSCHEMA)||'.'||rtrim(TABNAME) TAB_NAME, minute(current_timestamp-timestamp(LOCK_WAIT_START_TIME)) duration from SYSIBMADM.LOCKWAITS where minute(current_timestamp-timestamp(LOCK_WAIT_START_TIME))>2"
==============================================================================================================================死锁(dead-lock):
----------------------------------------------------------------------查找死锁(dead-lock):
可以通过事件监控器来查找死锁:
db2 "create event monitor dlockevm for deadlocks with details history write to file '/home/db2inst1/deadlock' " --DB2 9.7之前
db2 "create event monitor lockevmon for locking write to unformatted event table (table locks)" --DB2 9.7及之后
然后通过db2evmon工具可以获得死锁信息的日志:
db2evmon –path /home/db2inst1/deadlock > deadlock.txt
分析输出文件deadlock.txt
------------------------------查看锁表:
方法一:
db2 "SELECT * FROM TABLE( SNAPSHOT_LOCK( 'sample', -1 )) as SNAPSHOT_LOCK"| grep OPP_DETAIL_WEEKLY_MQT
db2 "SELECT * FROM TABLE( SNAPSHOT_LOCK( 'sample', -1 )) as SNAPSHOT_LOCK"|
方法二:
db2top -d sample -i 20
按U监控locks
找出第一列 Agent Id(State)
db2 get snapshot for application agentid 13373
按L可以显示lock chain
方法三:
db2pd -db sample -locks showlock wait
会得到类似: ....Rows(行锁)/X(排他锁) .....TbspaceID 3 TableID 12.... 或者 ...(obj={2;2828})
然后 db2 "select tbspace,tabschema,tabname,tableid,tbspaceid from syscat.tables where tbspaceid=3 and tableid=12"
或者 db2 "select tbspace,tabschema,tabname,tableid,tbspaceid from syscat.tables where tbspaceid=2 and tableid=2828"
方法四:
db2 get snapshot for locks on sample
找到Application handle(如: 29207)
db2 get snapshot for locks for application agentid 29207
方法五:
Select substr(tabschema,1,8) as tabschema, substr(tabname,1,15) as tabname,lock_object_type,
lock_mode, lock_mode_requested, agent_id_holding_lk
From sysibmadm.lockwaits
方法四的说明:
-------------------------------查看总体锁信息:
db2 get snapshot for database on sample|more
.........
Number of Threshold Violations = 0
Locks held currently = 0 当前锁的数量
Lock waits = 27 锁等个数
Time database waited on locks (ms) = 795431 表示花费在锁等时间上的总和(除以Lock waits即可算出每个锁等的平均时间)
Lock list memory in use (Bytes) = 5952 当前使用的锁内存大小
Deadlocks detected = 0 死锁个数
Lock escalations = 0 锁升级个数
Exclusive lock escalations = 0 排他锁升级个数
Agents currently waiting on locks = 0 表示当前正在等待锁的应用数
Lock Timeouts = 1 锁超时个数
Number of indoubt transactions = 0
..............
......
------------------------------------只看锁(多个时间段)
db2 get snapshot for locks on sb1dm|more
Application handle = 38321
Application ID = *LOCAL.DB2.150421201313
Sequence number = 00001
Application name = db2wlmd
CONNECT Authorization ID = DB2EIW01
Application status = Connect Completed
Status change time = 04/21/2015 16:13:09.375760
Application code page = 1208
Locks held = 0
Total wait time (ms) = 0
------------------------------也可以写成存储过程,可以利用存储过程来判断是否有死锁:
1.
db2 "CALL OMATIC.LOCK_APP_HOLD('EDGEDM.OPP_DETAIL_HISTORY_FACTS',?)"
如:
Value of output parameters
--------------------------
Parameter Name : O_APP_HANDLE
Parameter Value :
Return Status = 0
说明: Parameter Value :后面没有值说明没有lock
2.
db2 -x "CALL OMATIC.LOCK_INFO('EDGEDM.OPP_DETAIL_HISTORY_FACTS')" | grep user | sed 's/[ t]*$//g'
---------------------------------------------------------监控锁(等)参考同文件夹下word:查找锁等_例子.docx
db2pd –d <db_name> -locks wait –tra –app –dyn
db2pd -d sb1dm -locks wait -tra -app -dyn > lock_sb1dm.inf
db2pd -d sb1dm -locks showlocks wait -tra -app -dyn > lock_det_sb1dm.inf
这样可以查出表的具体sql
=============================================================================================================================手工上锁,死锁:
db2 lock table tablename in exclusive mode
db2 lock table tablename in share mode
db2 +c lock table tablename in exclusive mode
=============================================================================================================================锁超时:
如果锁持有者一直不提交,锁的请求者不能一直等下去,这就需要设置一个锁超时时间,LOCKTIMEOUT,若为-1则表示一直等下去,交易系统建议设置时间短一些(30--60s),仓库系统可以长一些。
-----------------------------------------锁超时时间:
db2 get db cfg|grep -i lock
.........
Lock timeout (sec) (LOCKTIMEOUT) = 600
............
修改锁超时时间:
db2 update db cfg for LOCKTIMEOUT using 60
=============================================================================================================================锁升级:
锁升级就是由行锁转换为表锁,从而减少锁的数量,释放内存资源的过程。 (因为所是一种内存资源,每个锁都需要占用一定的内存空间)
锁升级通过两个数据库参数locklist和maxlocks触发锁升级。locklist控制每个数据库可以使用的最大锁内存大小。maxlocks用来控制每个应用程序占用锁内存的百分比。
锁升级发生的条件:
1. 内存使用超过了locklist的大小。
2. 某个应用程序的锁的内存空间达到了locklist*maxlocks%
查找锁升级:
db2 get snapshot for database on sample
关键字:
Exclusive lock escalations = 0 排他锁升级个数
锁升级在诊断日志中会有记载,可以通过关键字escalation查找。
======================================================================================================锁转换:
锁转换是锁的模式的转换,是从弱类型的锁转换为强类型的过程,如S-->X, 使用场景如:一个事务读取某行数据,加了S锁,但是此时该事务需要更新此行,则需要获取X锁,S锁就转换为X锁。
-------------------------------------------表锁模式/锁类型:
弱类型锁:
IN 意向无(Intent None)
IS 意向共享锁(Intent Share)
IX 意向排他锁(Intent eXclusive)
SIX (Share with Intent eXclusive)
强类型锁:
S 共享锁(Share)
U 更新锁(Update)
X 排他锁(eXclusive)
Z 超级排他锁(Super eXclusive)
notes: 强类型锁适用于表中的所有的行,属于行锁
弱类型锁又叫意向锁,目的是为了配合行锁,在获得行锁之前先获得表锁,(属于表锁)。
-------------------------------------------------DB2锁的重要特性:已落实机制(Currently Committed, CC)
dst33lp14:/home/devdba$ db2 get db cfg for sample|grep -i cur_commit
Currently Committed (CUR_COMMIT) = ON
如果某个事务查询时,另外一个事务正在做增删改操作,那么查询事务需要等待增删改操作完成提交并且释放锁,才能查询。
Currently Committed是CS隔离级别的新实现(只针对默认的CS隔离级别),如果开启Currently Committed机制,则无需等待,会从日志中获取已落实版本的数据,即isnert,update,delete之前的数据,
无需等待。
修改CC参数:
db2 update db cfg for sample using CUR_COMMIT OFF
db2 update db cfg for sample using CUR_COMMIT ON
默认情况下CC参数是开启的。
------------------------------手工上锁,死锁:
db2 lock table tablename in exclusive mode
db2 lock table tablename in share mode
db2 +c lock table tablename in exclusive mode
db2 +c表示不自动提交。可以再执行commit提交或者rollback回滚。
-----------------------------创建一个锁等lock wait:
在一个窗口:
db2 +c "create table TEST.TEST_LK (i1 integer)"
在另一个窗口:
db2 "select * from TEST.TEST_LK"
---通过db2pd找出lock wait:
第三个窗口:
db2pd -database sample -locks -transactions -agents -file sample_lock2.txt
50684 -->50685
50684->51187
50684 51187
51187->51188
51187 51188