1. 程式人生 > >db2 避免死鎖和鎖等待

db2 避免死鎖和鎖等待

 

資料庫中之所以會存在死鎖或者鎖等待,是因為某一事務執行時間過長,導致鎖沒有及時釋放,那麼我們的解決辦法就是,事務過程儘量要短,並且事務中的sql執行要快,這樣才不會有過多的鎖等待。還有一個原因,就是一些執行糟糕的sql,比如走了全表掃描,那麼它會佔據表中大量的鎖,導致鎖住了其他行,其他使用者只能等待。

解決鎖等待,要注意以下幾點:

Ø  優化查詢 Sql,採用db2advis建立合適的索引,使得其能夠走索引查詢,由於索引的範圍和排序,可以直接跳過許其他行,定位到符合我們需要的行。

Ø  採用合適的隔離級別。由於DB2 預設是CS的隔離級別,它的原理是,遊標每到一行就會鎖住改行,對於一般應用來說是足夠了,但是如果遇到全表掃描,那麼CS模式會鎖住表中大量的行,直到查詢完畢。所以可以根據業務需求,將其改為UR模式,它不會對錶加任何行鎖。或者在JDBC中設定隔離級別(Isolation Levels)

Ø  合理設定鎖超時引數,它主要是用來避免事務長時間被佔用,導致鎖和連線無法釋放,影響系統的併發。可以設定 DB引數

Ø  更新操作一定要走索引,否則很容易產生死鎖。(針對邊更新邊查的操作)

Ø  避免出現鎖升級現象,當鎖等待達到一定程度時(行鎖的個數超過loctList *percent of lock list),就會出現行鎖升級為表鎖,即鎖升級。因為一旦出現鎖升級,那麼鎖住的就不再是行,而是表,那麼其他事務要想訪問該表中的任意行,必須等待事務將鎖釋放。

 修改Lock timeout (-1代表不檢測鎖超時),一般來說,該引數預設為10s足矣。
當系統存在嚴重的鎖等待時,可以通過以下sql,定位到鎖等待Sql

db2  "select AGENT_ID ,substr(STMT_TEXT,1,100) as statement,STMT_ELAPSED_TIME_MS  from table(SNAPSHOT_STATEMENT('dbname',-1)) as B where AGENT_ID in (select AGENT_ID_HOLDING_LK from table(SNAPSHOT_LOCKWAIT(‘dbname’,-1)) as A  order by  LOCK_WAIT_START_TIME ASC FETCH FIRST 20 ROWS ONLY ) order by STMT_ELAPSED_TIME_MS DESC"

執行結果如下:

        死鎖比鎖超時更加可怕,因為它將隨機回滾一個事務,而這個不受應用程式控制,不可控的錯誤十分可怕,所以一旦出現死鎖,必須解決掉。如何觀察DB2是否存在死鎖呢,有以下兩種方式:

1)  開啟lock快照監控

db2 update monitor switches using LOCK on

執行如下命令:

db2 get snapshot for database on dbname | grep -i "LOCK" ,結果如下:

 

可以看到其中有DeadLocks detected

2)  採用db2top 工具(db2 V9.1後才有)

Db2top –d dbname

然後鍵盤輸入‘d’,如下:

 可以看到其中有個 DeadLocks 計數。

如何定位死鎖也有很多方式,如:

1)  建立死鎖監控器(需要針對檔案分析,複雜度較高)

2)  採用db2pd(最穩定,一般可以定位到)

3)  採用db2top 監控元件(最快,但不一定能抓到)

附件中,講述方法2),該方法我在差旅測試過程中發現,並且總是可以精確定位到。

附上:如何精確定位死鎖

——誰佔有鎖,誰等待鎖,供開發參考

本次差旅發現過很多死鎖,有很多死鎖定位方式。但是能精確定位的還是比較少。通過本次差旅實踐,發現通過dbpd來捕捉是最好的,也是最精確的。方法我總結如下:

1)  啟用死鎖監控

db2pdcfg  –catch deadlock

 

當死鎖觸發時,會自動執行db2cos指令碼(在%db2dump%/bin 目錄下)。這個腳本里呼叫了db2pd來將當前資訊捕捉下來,其中主要捕捉的資訊包含如下:

 

Db2pd共捕捉了locks(鎖)、transaction(事務)、agents(代理程序)、application(應用)、dynamic(動態sql,這個最重要,是用於定位到sql)

2)  坐等死鎖。當死鎖觸發時,將自動生成 db2cos.process_id_application_id.txt,該檔案在db2的診斷目錄下(可以通過 db2 get dbm cfg | grep “DIA”來獲取診斷目錄位置)

 

3)  分析db2cos檔案:

a)       首先看-locks showlocks 模組,截圖如下:

 

找到其中包含【Sts】為W*,並且根據【ReleaseFlg】定位到相同slot的,類似,總共會有兩處,一處狀態為 W*(是死鎖)、一處是 G(代表鎖擁有)。Slot相同,就代表他們是在同一個資料物件上等待。尋找後的資料,如下截圖:

 

從上圖,可以看出如下資訊:

Ø  在tabled=514和tabspacesId=4上等待(可以在syscat.tables 檢視上根據資訊定位到表名稱 select TABNAME , TABSCHEMA from syscat.tables where TABLEID=514 and TBSPACEID=4)

Ø  佔據表鎖的transactionHandle Id 為 28,是X排他鎖

Ø  等待表鎖的transactionHandle Id為55,是NS(下一鍵共享鎖)

有了這兩個Id接著往下走。我們已經知道了誰在等待、誰在佔有。

b)      檢視-transaction,獲取transaction id對應的agent id

 

可以看到Apphandle對應值應有了,下面就需要根據這個Apphandle去找sql執行的資訊,已經離目標不遠了哈。

c)       檢視-dynamic 資訊

 

觀察如下列【C-AnchID】、【C-StmtUID】、【L-AnchID】、【L-StmtUID】

其中C-AnchID是代表當前正在執行的sql槽號,L-AnchID是代表上次執行的sql槽號。OK,我們就需要通過這個槽號來找到對應的Sql。定位到如下模組:

 

然後根據【C-AnchID】、【C-StmtUID】、【L-AnchID】、【L-StmtUID】列值找到對應sql(AnchID->C- AnchID,StmtUID-> C-StmtUID),如下:

 

自此,我們發現了導致死鎖的Sql:

哪個sql 在等待鎖?

狀態為W*:

SELECTCOUNT ( * )

  FROM    BIZ.WF_TASK A

       INNERJOIN

          BIZ.REI_FORM B

       ON A.RECEIPT_NO = B.REI_FORM_ID

 WHERE A.TASK_STATUS = ?

   AND A.HANDLE_ID = ?

   AND A.RECEIPT_TYPE = ?

   AND B.APPROVE_STATUS = ?

   AND B.REI_FORM_NO = ?

                            哪個Sql在佔有鎖?

       狀態為G:

       UPDATE BIZ.REI_FORM

   SET APPROVE_STATUS = ?

 WHERE REI_FORM_ID = ?

整個過程截圖如下:

 

定位到Sql之後,我們就可以按照第三節中敘述的方法,該建索引就建立索引,sql寫的不規範就調整sql。