用於鎖監視的 db2pd 選項
db2pd 是用於監視各種 DB2 數據庫活動以及故障排除的實用程序。它是從 DB2 V8.2 開始隨 DB2 引擎發布的一個獨立的實用程序,其外觀和功能類似於 Informix onstat 實用程序。db2pd 是從命令行以一種可選的交互模式執行的。該實用程序運行得非常快,因為它不需要獲取任何鎖,並且在引擎資源以外運行(這意味著它甚至能在一個掛起的引擎上工作)。通過快照監視還可以收集 db2pd 提供的很多監視器數據,但是 db2pd 和快照監視的輸出格式卻有很大不同。這使 DBA 可以選擇更符合用戶需求的監視替代方法。本文關注用於鎖監視的 db2pd 選項。
下面的圖展示了用於鎖監視的 db2pd 選項:
圖 1. 用於鎖監視的 db2pd 選項
TranHdl:用於指定事務句柄,以便只監視由特定事務持有的鎖。
showlocks:這個子選項將鎖名稱擴展成有意義的解釋。對於一個行鎖,該選項顯示以下信息:表空間 ID、表 ID、分區 ID、頁和槽。通過使用編目視圖 SYSCAT.TABLES 上的一個查詢,很容易將表空間 ID 和表 ID 映射到相應的表名:
清單 1. 將表空間 ID、表 ID 映射到表模式、表名
SELECT TABSCHEMA, TABNAME
FROM SYSCAT.TABLES
WHERE TBSPACEID = tbspaceid AND TABLEID = tableid
wait:如果指定 wait 子選項,則 db2pd 只顯示事務當前正在等待的鎖,以及對等待情形負責的鎖。這個子選項大大簡化了鎖等待分析,因為它將輸出限制為參與鎖等待情形的鎖。
db2pd database 和 file 選項不是特定於鎖監視的,但是適用於(幾乎)所有 db2pd 調用。database 選項將 db2pd 返回的監視器數據限制為某個數據庫的監視器數據。而 file 選項則允許定義一個文件,以便將 db2pd 輸出寫到該文件。
鎖等待分析場景
接下來,我們開始使用前面介紹的 db2pd 選項來分析一個示例鎖等待情形。為此,我們創建 DB2 SAMPLE 數據庫:
清單 2. 創建 SAMPLE 數據庫
db2sampl
用戶 A 執行事務 A,以根據每個經理的薪水為他們提供 10% 的獎金:
清單 3. 事務 A 執行的更新操作
UPDATE EMPLOYEE
SET BONUS = SALARY * 0.1
WHERE JOB = 'MANAGER'
當事務 A 仍然在運行(因為用戶 A 還沒有使用 COMMIT 或 ROLLBACK 終止該事務)時,用戶 B 執行事務 B,以將每個雇員的薪水提高 2%:
清單 4. 事務 B 執行的更新操作
UPDATE EMPLOYEE
SET SALARY = SALARY * 0.02
由於事務 B 沒有完成,用戶 B 請求 DBA 確定問題的原因。於是,DBA 調用 db2pd,看是否存在鎖等待情形:
清單 5. 檢查鎖等待情形
db2pd -db sample -locks wait showlocks
Database Partition 0 -- Database SAMPLE -- Active -- Up 3 days 08:33:05
Locks:
Address TranHdl Lockname Type Mode Sts Owner Dur
0x050A0240 6 02000600050040010000000052 Row ..X W 2 1
0x050A0DB0 2 02000600050040010000000052 Row ..X G 2 1
HoldCount Att ReleaseFlg
0 0x00 0x40000000 TbspaceID 2 TableID 6 PartitionID 0 Page 320 Slot 5
0 0x00 0x40000000 TbspaceID 2 TableID 6 PartitionID 0 Page 320 Slot 5
db2pd 報告 ID 為 2 的表空間中一個 ID 為 6 的表上有一個行鎖存在鎖等待情形。通過檢查 SYSCAT.TABLES,DBA 斷定表 EMPLOYEE 上的確存在鎖等待。
清單 6. 確定鎖等待情形所涉及的表
SELECT TABSCHEMA, TABNAME
FROM SYSCAT.TABLES
WHERE TBSPACEID = 2 AND TABLEID = 6
TABSCHEMA TABNAME
--------------------------------------------------------------------------------
FECHNER EMPLOYEE
1 record(s) selected.
對於事務 2(列 TranHdl),db2pd -locks 輸出的 status 列(Sts)顯示一個 “G”。G 代表 “granted”,意即事務句柄為 2 的事務擁有行鎖。此外,列 Mode 表明,事務 2 持有的是一個 X 鎖。等待的事務(列 Sts 中顯示 “W”(“wait”)的事務)是句柄為 6 的事務。該事務正在與事務 2 請求同一個行上的 X 鎖。通過查看 Owner 列(顯示事務 2 是鎖的所有者)和比較 Lockname(對於 db2pd -locks 中的兩個條目是相同的),可以看到這一點。
接下來,DBA 將事務句柄映射到應用程序。這可以使用另一個 db2pd 選項 -transactions 來完成:
清單 7. 將事務句柄映射到應用程序
db2pd -db sample -transactions
Database Partition 0 -- Database SAMPLE -- Active -- Up 3 days 08:34:47
Transactions:
Address AppHandl [nod-index] TranHdl Locks State Tflag Tflag2
0x05141880 30 [000-00030] 2 9 WRITE 0x00000000 0x00000
0x05144880 34 [000-00034] 6 5 WRITE 0x00000000 0x00000
這個 db2pd 調用的輸出表明,事務 2(列 TranHdl)是由應用程序 30(列 AppHandl)執行的,而事務 6 是由應用程序 34 執行的。這兩個事務都正在對數據庫執行寫更改(列 State = WRITE)。所以 DBA 現在知道,應用程序 30 正持有應用程序 34 所等待的鎖。
要獲得關於鎖等待情形涉及的應用程序的更多信息,可使用 -agents 選項調用 db2pd。該選項打印代表應用程序運行的代理的信息。注意,-agents 是一個實例級選項,這意味著不需要指定一個數據庫(實際上,當指定一個數據庫時,db2pd 打印出一條警告,並忽略 database 選項)。
清單 8. 獲得關於應用程序和相應代理的信息
db2pd -agents
Database Partition 0 -- Active -- Up 3 days 08:35:42
Agents:
Current agents: 2
Idle agents: 0
Active coord agents: 2
Active agents total: 2
Pooled coord agents: 0
Pooled agents total: 0
Address AppHandl [nod-index] AgentTid Priority Type State
0x04449BC0 34 [000-00034] 3392 0 Coord Inst-Active
0x04449240 30 [000-00030] 2576 0 Coord Inst-Active
ClientPid Userid ClIEntNm Rowsread Rowswrtn LkTmOt DBName
3916 USER_B db2bp.ex 43 43 NotSet SAMPLE
2524 USER_A db2bp.ex 153 14 NotSet SAMPLE
在 db2pd -agents 輸出中,DBA 可以看到使用應用程序 30 和 34 的用戶的 ID(列 Userid):應用程序 30 是由 USER_A 執行的,而應用程序 34 是由 USER_B 執行的。只有當每個用戶都有一個單獨的數據庫授權 ID 時,才可能出現那樣的應用程序與用戶 ID 之間的映射。通常,這對於在應用服務器上運行的應用程序是不可能的,因為這些應用程序使用連接池,連接不是個人化的。
關於每個應用程序的更多信息則由 db2pd 選項 -applications 提供:
清單 9. 獲得關於應用程序的更多信息
db2pd -db sample -applications
Database Partition 0 -- Database SAMPLE -- Active -- Up 3 days 08:36:14
Applications:
Address AppHandl [nod-index] NumAgents CoorTid Status
0x04AF8080 34 [000-00024] 1 3940 Lock-wait
0x03841960 30 [000-00020] 1 2548 UOW-Waiting
C-AnchID C-StmtUID L-AnchID L-StmtUID Appid
195 1 0 0 *LOCAL.DB2.061122195637
0 0 60 1 *LOCAL.DB2.061122195609
Status 列確認了 DBA 已經知道的一些東西:應用程序 34 處在鎖等待狀態。但是這並不新鮮,於是 DBA 將注意力集中在列 C-AnchID/C-StmtUID 和 L-AnchID/L-StmtUID 上。“C” 代表當前(current),“L” 代表最近(last)的錨 ID/語句 UID。這些 ID 可用於標識應用程序最近執行的 SQL 語句和應用程序當前執行的語句。為此,可以用 -dynamic 選項調用 db2pd。該選項顯示數據庫動態語句緩存的內容:
清單 10. 檢查動態語句緩存的內容
db2pd -db sample -dynamic
Database Partition 0 -- Database SAMPLE -- Active -- Up 3 days 08:37:39
Dynamic Cache:
Current Memory Used 187188
Total Heap Size 1271398
Cache Overflow Flag 0
Number of References 2
Number of Statement Inserts 3
Number of Statement Deletes 0
Number of Variation Inserts 2
Number of Statements 3
Dynamic SQL Statements:
Address AnchID StmtUID NumEnv NumVar NumRef NumExe
0x056CEBD0 60 1 1 1 1 1
0x056CE850 180 1 0 0 0 0
0x056CFEA0 195 1 1 1 1 1
Text
UPDATE EMPLOYEE SET BONUS = SALARY * 0.1 WHERE JOB = 'MANAGER'
SET CURRENT LOCALE LC_CTYPE = 'de_DE'
UPDATE EMPLOYEE SET SALARY = SALARY * 0.02
Dynamic SQL Environments:
Address AnchID StmtUID EnvID Iso QOpt Blk
0x056CECD0 60 1 1 CS 5 B
0x056D30A0 195 1 1 CS 5 B
Dynamic SQL Variations:
Address AnchID StmtUID EnvID VarID NumRef Typ
0x056CEEB0 60 1 1 1 1 4
0x056D3220 195 1 1 1 1 4
Lockname
010000000100000001003C0056
01000000010000000100C30056
-applications 輸出與 -dynamic 輸出之間的映射很簡單:
應用程序 34(處於鎖等待狀態)當前正在執行當前錨 ID 195 和當前語句 ID 1 所標識的 SQL 語句。在 db2pd -dynamic 輸出的 Dynamic SQL Statements 部分中,那些 ID 可以映射到以下 SQL 語句:
清單 11. 應用程序 34 執行的 SQL 語句
UPDATE EMPLOYEE SET SALARY = SALARY * 0.02
持有鎖的應用程序 30 最近執行的 SQL 語句是最近錨 ID 60 和最近語句 ID 1 所標識的 SQL 語句。那些 ID 可以映射到以下 SQL 語句:
清單 12. 應用程序 30 執行的 SQL 語句
UPDATE EMPLOYEE SET BONUS = SALARY * 0.1 WHERE JOB = 'MANAGER'
注意,db2pd -dynamic 輸出包含另一個通常難以發現的有趣信息:Dynamic SQL Environments 部分的列 Iso 中顯示了被執行的動態 SQL 語句的隔離級別(UR = Uncommitted Read,CS = Cursor Stability,RS = Read Stability,RR = Repeatable Read)。
我們來總結一下 DBA 就用戶 B 的應用程序被掛起的原因有什麼發現:
掛起是由表 EMPLOYEE 上一個獨占式的行鎖導致的。
持有鎖的事務屬於用戶 A 執行的一個應用程序。而用戶 B 的事務正在等待那個鎖。
兩條有沖突的語句都是表 EMPLOYEE 上的 UPDATE 語句。
有了這些信息,DBA 可以開始采取一些必要的步驟來解決鎖等待狀況,例如建議用戶 A 終止事務,或者強制關閉用戶 A 的應用程序。此外,可以采取措施避免將來出現那樣的狀況,例如配置 DB2 控制器(governor),使之自動終止運行時間過長的事務。
在這個示例場景中,db2pd 被連續執行數次,每次使用一個單獨的選項。現實中不會出現這樣的情況。相反,db2pd 只被調用一次,調用時同時使用前面介紹的所有選項:
清單 13. 分析鎖等待情形所需的帶有所有選項的單個 db2pd 調用
db2pd -db sample -locks wait showlocks -transactions -agents -applications -dynamic
-file db2pd.out -repeat 15 40
產生的輸出由針對每個選項的輸出組成,各部分輸出之間的順序與各選項在 db2pd 調用中的順序一致。而且,請注意 db2pd 調用最後的 2 個附加選項:
-file 表明 db2pd 輸出應該被寫到一個文件。在示例調用中,輸出被寫到文件 db2pd.out 中。
-repeat 表明 db2pd 應該每隔 15 秒執行一次,共執行 40 次(即每隔 15 秒執行一次,共執行 10 分鐘)。每次執行的輸出被附加到 -file 選項指定的文件後面。
-file 和 -repeat 選項對於在一段時間內監視數據庫活動比較有用。對於鎖等待分析,這兩個選項可以幫助捕捉只存在一小段時間的鎖等待情形。例如,如果數據庫參數 LOCKWAIT 被設置為 20 秒,一個等待鎖的事務在過了 20 秒的等待時間後被回滾。為了捕捉那樣的鎖等待情形,db2pd 的時間間隔必須設置為比 20 秒更短的時間間隔,例如例子中的 15 秒。
捕捉罕見的鎖超時
有時候,鎖等待情形會導致鎖超時,而鎖超時又會導致事務被回滾。鎖等待導致鎖超時所需的時間段由數據庫配置參數 LOCKTIMEOUT 指定。鎖超時分析最大的問題是,不知道下一次的鎖超時何時發生。為了捕捉死鎖,可以創建一個死鎖事件監視器。每當出現死鎖時,這個死鎖事件監視器便寫一個條目。但是,對於鎖超時就沒有類似的事件監視器。所以到 DB2 9® 為止,捕捉鎖超時的惟一方法還是連續的 db2pd 或快照監視(對於 db2pd,和前面解釋的一樣,-file 和 -repeat 選項可用於連續的鎖監視)。
DB2 9 包含了一種新的機制,用於在數據庫出現故障或發生事件時收集監視器數據:db2cos 腳本。為了捕捉鎖超時事件,可以配置數據庫,使之每當出現鎖超時時啟動 db2cos 腳本。在 db2cos 腳本中,和前面討論的一樣,可以以相同的選項調用 db2pd。我們來看一個示例場景,該場景演示了如何用 db2cos 腳本捕捉鎖超時。
對於這個場景,假設 DBA 將數據庫鎖超時值設為 10 秒:
清單 14. 更新鎖超時設置
UPDATE DB CFG FOR SAMPLE USING LOCKTIMEOUT 10
為了每當出現鎖超時時啟動 db2cos 腳本,DBA 調用 db2pdcfg 實用程序,如下所示:
清單 15. 使用 db2pdcfg 配置 db2cos 腳本的調用
db2pdcfg -catch locktimeout count=1
-catch 選項指定應該自動導致調用 db2cos 腳本的故障或事件。對於鎖超時事件,可以指定字符串 locktimeout。或者,可以指定與鎖超時相應的 SQL 錯誤碼和原因碼:
清單 16. 用於捕捉鎖超時的另一種 db2pdcfg 調用
db2pdcfg -catch 911,68 count=1
除了一些字符串值和 SQL 代碼之外,db2pdcfg 還接受內部 DB2 錯誤碼。所以,用這種方式可以捕捉很多數據庫故障和事件。鎖超時事件只是使用 db2pdcfg 和 db2cos 的一個例子。
如果 count 子選項的值為 1,則表明當出現鎖超時事件時應該執行 db2cos 腳本。
db2pdcfg 通過以下輸出確認錯誤捕捉的設置:
清單 17. db2pdcfg 對錯誤捕捉設置的確認
Error Catch #1
Sqlcode: 0
ReasonCode: 0
ZRC: -2146435004
ECF: 0
Component ID: 0
LockName: Not Set
LockType: Not Set
Current Count: 0
Max Count: 1
Bitmap: 0x4A1
Action: Error code catch flag enabled
Action: Execute sqllib/db2cos callout script
Action: Produce stack trace in db2diag.log
db2diag.log 報告中也包括錯誤捕捉設置。可以使用 db2diag 實用程序(用於檢查 db2diag.log 內容的一個有用的實用程序)過濾 db2diag.log 文件,而不必在一個文本編輯器中打開它:
清單 18. 在 db2diag.log 中確認錯誤捕捉設置
db2diag -g funcname:=pdErrorCatch
2006-12-18-13.37.25.177000+060 I727480H285 LEVEL: Event
PID : 4648 TID : 3948 PROC : db2syscs.exe
INSTANCE: DB2 NODE : 000
FUNCTION: DB2 UDB, RAS/PD component, pdErrorCatch, probe:30
START : Error catch set for ZRC -2146435004
ZRC -2146435004 是用於鎖超時的 DB2 內部錯誤碼。可以通過下面的 db2diag 調用查看這些錯誤碼:
清單 19. 使用 db2diag 查看 DB2 內部錯誤碼的含義
db2diag -rc -2146435004
通過使用 db2pdcfg,數據庫引擎現在被配置為每當出現鎖超時時調用 db2cos 腳本。db2cos 腳本收集判別鎖超時原因所需的所有監視器信息。為此,DBA 必須修改 db2cos 腳本,以便用已知的選項調用 db2pd。可以在下面的子目錄中找到 db2cos 腳本:
Windows:DB2 install directoryBINdb2cos.bat,例如 C:Program FilesIBMSQLLIBBINdb2cos.bat
UNIX/Linux:Instance owner home/sqllib/bin/db2cos
在 Microsoft Windows® 上,默認的 db2cos.bat 腳本看上去如下所示:
清單 20. Windows 上默認 db2cos.bat 的內容
setlocal
:iterargs
if %0. == . goto iterdone
if /i %0. == INSTANCE. set INSTANCE=%1
if /i %0. == DATABASE. set DATABASE=%1
if /i %0. == TIMESTAMP. set TIMESTAMP=%1
if /i %0. == APPID. set APPID=%1
if /i %0. == PID. set PID=%1
if /i %0. == TID. set TID=%1
if /i %0. == DBPART. set DBPART=%1
if /i %0. == PROBE. set PROBE=%1
if /i %0. == FUNCTION. set FUNCTION=%1
if /i %0. == REASON. set REASON=%1
if /i %0. == DESCRIPTION. set DESCRIPTION=%1
if /i %0. == DiAGPATH. set DIAGPATH=%1
shift
goto iterargs
:iterdone
if %DATABASE%. == . goto no_database
db2pd -db %DATABASE% -inst >> %DIAGPATH%db2cos%PID%%TID%.%DBPART%
goto exit
:no_database
db2pd -inst >> %DIAGPATH%db2cos%PID%%TID%.%DBPART%
:exit
對於數據庫級的事件或故障,默認的 db2cos 腳本用 -db 和 -inst 選項調用 db2pd。DBA 用一個 db2pd 調用替換相應的行,該調用收集鎖超時分析所需的監視器數據:
清單 21. 更改 db2cos 腳本,以收集用於鎖超時分析的數據
if %DATABASE%. == . goto no_database
db2pd -db %DATABASE% -locks wait -transactions -agents -applications -dynamic
>> %DIAGPATH%db2cos%PID%%TID%.%DBPART%
goto exit
現在,db2cos 腳本已准備好,DBA 可以坐等下一次鎖超時事件的發生。
假設像之前描述的那樣,用戶 A 與 B 之間發生相同的鎖情形。但是,這一次設置了 LOCKTIMEOUT,因此過了 10 秒(LOCKTIMEOUT = 10)之後用戶 B 的事務被回滾。用戶 B 通知 DBA 回滾自己的事務,並且收到 SQL 錯誤消息 -911 和原因碼 68(SQL code -911 / reason code 68 = locktimeout)。於是,DBA 檢查通過自動調用 db2cos 腳本收集到的監視器數據。
首先,DBA 用鎖超時內部錯誤碼調用 db2diag,以確定鎖超時發生的確切時間:
清單 22. 在 db2diag.log 中檢查鎖超時事件的時間點
db2diag -g data:=-2146435004
2006-12-18-14.27.24.656000+060 I6857H409 LEVEL: Event
PID : 2968 TID : 2932 PROC : db2syscs.exe
INSTANCE: DB2 NODE : 000 DB : SAMPLE
APPHDL : 0-21 APPID: *LOCAL.DB2.061226132544
AUTHID : FECHNER
FUNCTION: DB2 UDB, lock manager, sqlplnfd, probe:999
DATA #1 : <preformatted>
Caught rc -2146435004. Dumping stack trace.
db2diag.log 條目顯示,在 2006-12-18-14.27.24.656000 時發生了一次鎖超時。由於 db2cos 腳本將它的輸出寫到 %DIAGPATH% 中的 db2cos%PID%%TID%.%DBPART% 文件中,DBA 有望在實例的診斷路徑中找到一個 db2cos29682932.0 文件:
%DIAGPATH% = instance's diagnostic path = on Windows by default C:Program FilesIBMSQLLIBDB2
%PID% = process ID = 2968(如 db2diag.log 條目中所示)
%TID% = thread ID = 2932(也顯示在 db2diag.log 條目中)
%DBPART% = database partition = 0(在一個非分區數據庫環境中)
那個文件的內容很像本文第一部分中逐步考察的那個 db2pd 監視器輸出,DBA 可以通過它來識別鎖超時的原因。
捕捉到鎖超時後,DBA 可以通過 -catch clear 選項調用 db2pdcfg 來禁用 db2cos 腳本:
清單 23. 再次使用 db2pdcfg 清除錯誤捕捉設置
db2pdcfg -catch clear
All error catch flag settings cleared.
結束語
本文演示了如何使用 db2pd 實用程序進行鎖等待監視。本文借助一個示例場景展示了 DBA 如何通過檢查不同 db2pd 選項的輸出來識別並發問題的原因。從 DB2 9 開始,可以將 db2pd 與新的 db2cos 腳本一起使用,以便在出現鎖超時事件時捕捉它們。您學習了如何配置出現鎖超時事件時 db2cos 腳本的自動調用。本文還介紹了 db2diag 實用程序 —— 這是用於檢查 db2diag.log 內容的一個有用的工具。