DB2 鎖問題分析與解釋
DB2 應用中常常會遇到鎖超時與死鎖現象,那麽這樣的現象產生的原因是什麽呢。本文以試驗的形式模擬鎖等待、鎖超時、死鎖現象。並給出這些現象的根本原因。
試驗環境:
DB2 v9.7.0.6
AIX 6.1.0.0
採用默認的隔離級別CS
STUDENT表的DDL與初始內容
------------------------------------------------
-- DDL Statements for table "E97Q6C "."STUDENT"
------------------------------------------------
CREATE TABLE "E97Q6C "."STUDENT" (
"AGE" INTEGER ,
"NAME" CHAR(8) )
IN "USERSPACE1" ;
$ db2 "select * from student"
AGE NAME
----------- --------
3 xu
5 gao
2 liu
1 gu
試驗1:驗證insert操作與其它操作的鎖等待問題
session 1中發出insert操作,在session 2中觀察insert,update,delete操作是否會鎖超時。
session 1
---------
$ db2 +c "insert into student values(4, ‘miao‘)"
DB20000I The SQL command completed successfully.
session 2
---------
$ db2 "insert into student values(6, ‘mu‘)"
DB20000I The SQL command completed successfully
$ db2 "update student set name=‘gu‘ where age=1"
DB20000I The SQL command completed successfully.
$ db2 "delete from student where age=2"
DB20000I The SQL command completed successfully.
----------------------------------------------------------------------------
結論1:當session 1對表作insert操作時,session 2對該表的insert及其它行的update,delete操作都不會有問題----------------------------------------------------------------------------
試驗2:驗證update操作與其它操作的鎖等待問題
session 1中發出update操作,在session 2中觀察insert,update,delete操作是否會鎖超時。
--------------
session 1
---------
$ db2 commit
$ db2 "select * from student"
AGE NAME
----------- --------
3 xu
5 gao
6 mu
4 miao
1 gu
5 record(s) selected.
$ db2 +c "update student set name = ‘qing‘ where age=4"
DB20000I The SQL command completed successfully.
session 2
---------
$ db2 "insert into student values(6, ‘mu‘)"
DB20000I The SQL command completed successfully.
$ db2 "update student set name=‘gu‘ where age=1"
DB21034E The command was processed as an SQL statement because it was not a
valid Command Line Processor command. During SQL processing it returned:
SQL0911N The current transaction has been rolled back because of a deadlock
or timeout. Reason code "68". SQLSTATE=40001
$ db2 "delete from student where age=2"
DB21034E The command was processed as an SQL statement because it was not a
valid Command Line Processor command. During SQL processing it returned:
SQL0911N The current transaction has been rolled back because of a deadlock
or timeout. Reason code "68". SQLSTATE=40001
----------------------------------------------------------------------------
結論2:當session 1對表某一行做update操作時,session 2能夠對該表作insert操作,但不同意對其它行的delete和update操作
----------------------------------------------------------------------------
試驗3:驗證delete操作與其它操作的鎖等待問題
session 1中發出delete操作,在session 2中觀察insert,update,delete操作是否會鎖超時。
---------
$ db2 commit
$ db2 +c "delete from student where age=4"
DB20000I The SQL command completed successfully.
session 2
---------
$ db2 "insert into student values(6, ‘mu‘)"
DB20000I The SQL command completed successfully.
$ db2 "update student set name=‘gu‘ where age=1"
DB21034E The command was processed as an SQL statement because it was not a
valid Command Line Processor command. During SQL processing it returned:
SQL0911N The current transaction has been rolled back because of a deadlock
or timeout. Reason code "68". SQLSTATE=40001
$ db2 "delete from student where age=2"
DB21034E The command was processed as an SQL statement because it was not a
valid Command Line Processor command. During SQL processing it returned:
SQL0911N The current transaction has been rolled back because of a deadlock
or timeout. Reason code "68". SQLSTATE=40001
----------------------------------------------------------------------------
結論3:當應用1對表某一行做delete操作時,應用2能夠對該表作insert操作。但不同意對其它行的delete和update操作
----------------------------------------------------------------------------
總的結論是:
應用對表作insert操作時。其它操作不受影響,也不受其它操作影響。
作update,delete操作時,其它的update和delete操作受影響。
為了解釋以上現象的原因,我們首先看一下上面的操作須要什麽樣的鎖。
session 1.
---------
$ db2 rollback
$ db2 +c "insert into student values(7,‘han‘)"
DB20000I The SQL command completed successfully.
$ db2pd -db qsmiao -locks
結論:insert操作須要表級的IX鎖和行級的X鎖。
註:IX鎖。該鎖的擁有者在擁有對應行的X鎖時能夠更改該行的數據。
$ db2 rollback
$ db2 +c "update student set name=‘yan‘ where age=5"
DB20000I The SQL command completed successfully.
$ db2pd -db qsmiao -locks
結論:update操作須要表級的IX鎖和行級的X鎖。
$ db2 rollback
$ db2 +c "delete from student where age=6"
DB20000I The SQL command completed successfully.
$ db2pd -db qsmiao -locks
結論:update操作須要表級的IX鎖和相應的行級的X鎖(這裏由於3條記錄的age都為6,因此須要3個行級鎖)。
如今的問題是:為什麽insert和update,delete操作須要的鎖一樣(表級的IX鎖。相應行級的X鎖),可是表現的效果卻不一樣呢?
為了解決問題。看一下他們的運行計劃吧:
$ db2expln -d qsmiao -g -statement "insert into student values(5, ‘gao‘)" -terminal
$ db2expln -d qsmiao -g -statement "update student set name=‘qing‘ where age=4" -terminal
$ db2expln -d qsmiao -g -statement "delete from student where age=6" -terminal
從上面的運行計劃中能夠看到原因:insert操作不須要表掃描,而update和delete操作都須要全表掃描,並且會在掃描的時候試圖對每一行加U鎖。
導致鎖超時的原因就是表掃描
比如session 1要更新表的某一行,會在該行加上X鎖。之後, session 2試圖更新該表的還有一行,進行全表掃描時,就會試圖對A占用的那一行加上U鎖,但無能為力,終於導
致鎖超時。
session 1
---------
$ db2 +c "update student set name=‘hehe‘ where age = 4"
DB20000I The SQL command completed successfully.
session 2
---------
$ db2 +c "delete from student where age=6"
<-------這時會hang住。由於它在等session 1的鎖
session 3
---------
$ db2pd -db qsmiao -wlocks <---在鎖超時發生之前,抓取鎖等待的消息
Locks being waited on :
AppHandl [nod-index] TranHdl Lockname Type Mode Conv Sts CoorEDU AppName AuthID AppID
15393 [000-15393] 2 00020004000000000000000952 Row ..X G 7818 db2bp E97Q6C *LOCAL.e97q6c.141016035113
15408 [000-15408] 16 00020004000000000000000952 Row ..U W 10153 db2bp E97Q6C *LOCAL.e97q6c.141016035219
能夠看到,是由於U鎖和X鎖的不兼容導致鎖等待,最後導致鎖超時。
為了解決該鎖等待問題,能夠在查詢謂詞所涉及的列age上建立索引,避免全表掃描
試驗4:通過建立索引,消除鎖等待現象
session 1
---------
$ db2 rollback
$ db2 +c "lock table student in share mode"
$ db2 +c "create index stu_idx on student(age)"
$ db2 commit
$ db2 +c "update student set name=‘hehe‘ where age = 4"
DB20000I The SQL command completed successfully.
session 2
---------
$ db2 +c "delete from student where age=6" <--沒有發生鎖等待現象,直接成功
DB20000I The SQL command completed successfully.
能夠看到,已經通過索引攻克了該鎖超時問題。假設讀者有興趣的話,能夠看下建立索引之後的訪問計劃。
以下模擬一個死鎖現象
試驗5:模擬死鎖,步驟例如以下
第一步:session 1 獲得 鎖 LOCK1
第二步:session 2 獲得 鎖 LOCK2
第三步:session 2 申請 鎖 LOCK1
第四步:session 1 申請 鎖 LOCK2
為了避免死鎖之前產生鎖超時,先將鎖超時控制參數設為-1(表示永遠等待)
update db cfg using locktimeout -1
之後重新啟動數據庫
session 1
---------
$ db2 +c "update student set name = ‘an‘ where age = 1" <--獲得鎖LOCK1,成功
DB20000I The SQL command completed successfully.
session 2
---------
$ db2 +c "update student set name = ‘two‘ where age = 4" <--獲得鎖LOCK2,成功
DB20000I The SQL command completed successfully.
$ db2 +c "update student set name = ‘four‘ where age = 1" <--申請鎖LOCK1,hang住。由於LOCK1被session 1持有
session 1
---------
$ db2 +c "update student set name = ‘three‘ where age = 4" <--申請鎖LOCK2,hang住,由於LOCK2被session 2持有
這時已經發生了死鎖,10s之後,這兩個session有一個會報出例如以下死鎖(reason code 2)錯誤。還有一個session成功運行
SQL0911N The current transaction has been rolled back because of a deadlock
or timeout. Reason code "2". SQLSTATE=40001
參考資料:
標準表的鎖定方式和存取方案,這裏您能夠看到具體的加鎖方式
http://www-01.ibm.com/support/knowledgecenter/SSEPGG_9.7.0/com.ibm.db2.luw.admin.perf.doc/doc/r0005275.html?lang=zh
附。僅僅能在發生死鎖或者鎖等待的時候才幹用db2pd查看鎖的信息。以下附上怎樣採用事件監控器監控死鎖/鎖超時。事件監控器能夠抓取一段時間內的鎖事件
db2 update db cfg for sample using MON_LOCKWAIT hist_and_values MON_DEADLOCK hist_and_values MON_LOCKTIMEOUT hist_and_values MON_LW_THRESH 10000
db2 "CREATE EVENT MONITOR LOCKEVMON FOR LOCKING WRITE TO UNFORMATTED EVENT TABLE (TABLE LOCKEVMON)"
db2 set event monitor LOCKEVMON state=1
重現問題
db2 flush event monitor LOCKEVMON
db2 set event monitor LOCKEVMON state=0
cp /home/db2users/e97q6c/sqllib/samples/java/jdbc/db2evmonfmt.java ./
cp /home/db2users/e97q6c/sqllib/samples/java/jdbc/DB2EvmonLocking.xsl ./
export PATH=/home/db2users/e97q6c/sqllib/java/jdk64/bin:$PATH
javac db2evmonfmt.java
java db2evmonfmt -d sample -ue LOCKEVMON -ftext -u e97q6c -p e97q6c > deadlock.txt
more deadlock.txt 能夠看到有關的SQL語句。
請參考
http://www.ibm.com/developerworks/cn/data/library/techarticles/dm-1004lockeventmonitor/
http://blog.csdn.net/qingsong3333/article/details/51206329
DB2 鎖問題分析與解釋