1、概念描述程序員
所謂的鎖升級(lock escalation),是數據庫的一種做用機制,爲了節約內存的開銷, 其會將爲數衆多並佔用大量資源的細粒度的鎖轉化爲數量較少的且佔用相對較少資源的粗粒度的鎖,多數狀況下主要指將爲數衆多的行鎖升級爲一個表鎖。 固然,DB2 支持不少粒度的鎖,如表空間(table space),表(table),行(row)以及索引(index)等。sql
通常涉及到鎖升級優化的參數調整,涉及的參數有如下幾個:數據庫
LOCKTIMEOUT併發
LOCKLISTapp
MAXLOCKSide
下面咱們大概瞭解一下這幾個參數的定義:工具
LOCKTIMEOUT(鎖定超時)----此參數指定應用程序爲獲取一個鎖定將等待的秒數,以幫助避免應用程序出現全局死鎖。默認值-1 [-1; 0 - 32 767 ]性能
LOCKLIST----此參數指示分配給鎖定列表的內存量。每一個數據庫都有一個鎖定列表,鎖列表包含了併發鏈接到該數據庫的全部應用程序所持有的鎖。鎖定是數據庫管理器用來控制多個應用程序併發訪問數據庫中數據的機制。行和表均可以被鎖定。默認值automatic [4 – 524288]優化
MAXLOCKS----此參數定義應用程序掛起的鎖定列表的百分比,必須在數據庫管理器執行鎖定升級以前填寫該列表。當任何一個應用程序持有的鎖數量達到這個百分比時,會選取「行鎖最多」的表進行鎖升級。默認值 automatic [1 – 100]ui
經過如下方法,可查看每一個數據庫對於鎖的相應配置
$ db2 get db cfg for <dbname>|grep -i lock
Max storage for lock list (4KB) (LOCKLIST) = 4096
Percent. of lock lists per application (MAXLOCKS) = 10
Interval for checking deadlock (ms) (DLCHKTIME) = 10000
Lock timeout (sec) (LOCKTIMEOUT) = -1
Block log on disk full (BLK_LOG_DSK_FUL) = NO
Block non logged operations (BLOCKNONLOGGED) = NO
Lock timeout events (MON_LOCKTIMEOUT) = NONE
Deadlock events (MON_DEADLOCK) = WITHOUT_HIST
Lock wait events (MON_LOCKWAIT) = NONE
Lock wait event threshold (MON_LW_THRESH) = 5000000
Lock event notification level (MON_LCK_MSG_LVL) = 1
2、鎖升級的產生及影響
何時會發生鎖升級呢?
其實對每個鎖,DB2 數據庫都要耗費必定的內存資源來管理並維護(通常狀況下,對某個對象上所加的第一個鎖須要 256 字節,而在該對象上所加的鎖從第二個鎖開始就只須要 128 字節了)。 所以,若是在一個表上,有大量的行鎖被請求時,爲了節約數據庫資源的佔用,「聰明的」數據庫管家會用一個鎖住整個表的表鎖來代替爲數衆多的行鎖,從而釋放了本來大量行鎖所佔用的資源。 而這個過程,就被稱之爲鎖升級。那麼,數據庫何時會將行鎖自動升級爲表鎖、鎖升級遵循怎樣的規律、該如何預測鎖升級的發生呢? 這裏就須要提到兩個影響數據庫鎖升級的 DB2 數據庫配置參數:
DB2 數據庫主要在如下兩種情形時會進行鎖升級:
1) 當一個應用的鎖所使用的內存 >LOCKLIST × MAXLOCKS
2) 多個應用的鎖使用的內存 >LOCKLIST
那若是發生鎖升級了,會有什麼影響?
因爲這是數據庫自行控制的機制,咱們在不經意之間享受到了好處的同時,也經常受到該機制的困擾。 顯而易見,一旦下降了系統併發性及性能,並行改串行,性能會隨之下降。性能下降可由如下幾個可能的緣由產生:
不一樣事務對於同一張表引起的鎖升級會誘發死鎖(deadlock)
在鎖升級發生後,因爲同表的併發請求被強制轉換成串行處理,若是鎖等待的時間不是足夠長的話,會被數據庫「誤判」爲 lock waiting timeout,從而誤導程序員判斷問題的根本緣由。此時,經常會被認爲是因爲死鎖引發的鎖等待時間過長
當一個應用程序使用的LOCKLIST的百分比達到MAXLOCKS時,數據庫管理器將執行一次鎖升級(lock escalation),在這個操做中將使行鎖轉換成單獨的一個表鎖。並且,若是LOCKLIST快要耗盡,數據庫管理器將找出持有一個表上最多行鎖的鏈接,並將這些行鎖轉換成表鎖,以釋放LOCKLIST內存。鎖定整個表會大大下降併發性,死鎖的概率也就增長了。
鎖升級的問題,因爲通常不在應用程序日誌裏面進行記錄,因此很難被捕獲到。幸運的是DB2提供了多種類型的日誌以及一些數據庫工具來確認和定位相似問題的發生。
如下將介紹幾種關於數據庫鎖升級問題的探知方法供各位參考。有的是基於事件捕獲型的,有的是基於統計的,可經過自行判斷用哪一種方式適合本身:
3、鎖監控部署
日誌路徑(默認):$/db2home/db2inst1/sqllib/db2dump/db2inst1.nfy
預置條件:須要預先打開snapshot的 lock monitor;在 DB2 v9.7 以後,能夠 set mon_lck_msg_lvl = 1(咱們下面的示例版本是DB2 v9.1)
下面是該通知日誌的一個鎖升級log示例
2017-03-18-01.34.29.630201 Instance:db2inst1 Node:001
PID:28236(db2agntp (BASSDB) 1) TID:1 Appid:172.16.5.54.54061.170317172607
data management sqldEscalateLocks Probe:2
ADM5500W DB2 is performing lock escalation. The total number of locks
currently held is 「57630」, and the target number of locks to hold is 「28815」.
日誌路徑(默認):$/db2home/db2inst1/sqllib/db2dump/db2diag.log
預置條件:須要預先打開snapshot的 lock monitor;在 v9.7 以後,能夠 set mon_lck_msg_lvl = 1(咱們下面的示例版本是DB2 v9.1)
下面是該診斷日誌的一個鎖升級log示例
2017-03-18-01.34.29.667386+480 E10178829A480 LEVEL: Warning
PID : 28236(db2agntp (BASSDB) 1) TID : 1 PROC: db2agntp (BASSDB) 1
Instance : db2inst1 Node :001
APPHDL : 0-22-1 Appid:172.16.5.54.54061.170317172607
AUTHID : BASS2
FUNCTION : DB2 UDB, data management sqldEscalateLocks, probe:3
MESSAGE :ADM5502W The escalation of 「57624」 locks on table 「BASS2.DWD_CUST_RELATION_20170317」 to lock intent 「X」 was successful.
DB2 數據庫快照能夠用來採集一段時間範圍內數據庫活動的一些統計信息以及某個時間點數據庫的狀態信息等
打開監視器:db2 -v update monitor switches using lock on
啓用監視器:db2 -v commit / db2 -v terminate
收集快照:db2 -v get snapshot for database on bassdb | grep -i lock
下面是輸出樣例
Locks held currently = 2541
Lock waits = 38884
Time database waited on locks (ms) = 659308372
Lock list memory in use (Bytes) = 648832
Deadlocks detected = 110
Lock escalations = 0
Exclusive lock escalations = 0
Agents currently waiting on locks = 0
Lock Timeouts = 159
Internal rollbacks due to deadlock = 327
Memory Pool Type = Lock Manager Heap
4、鎖升級優化
鎖升級問題發生後如何經過參數設置來優化?
1. 鎖升級問題能夠經過增長LOCKLIST和MAXLOCKS數據庫參數的大小來解決。可是,若是仍然遇到鎖定問題,應檢查是否因未能提交事務而未釋放已更新行上的鎖。
2. LOCKLIST配置參數的計算方法以下(操做系統爲64位平臺):
(1) 計算鎖列表大小的下限:(512 * 32 * MAXAPPLS)/4096。其中,512是每一個應用程序平均所含鎖數量的估計值,32是對象(已有一把鎖)上每把鎖所需的字節數。(在 32 位平臺上須要 40 位,那麼 64 位平臺上須要 64 位)。
(2) 計算鎖列表大小的上限:(512 * 128 * MAXAPPLS)/4096。其中,128是某個對象上第一把鎖所需的字節數。(在 32 位平臺上須要 80 位,在 64 位平臺上須要 128 位)。
(3)對於您的數據,估計可能具備的併發數,並根據您的預計爲鎖列表選擇一個初始值,該值位於您計算出的上限和下限之間。
3. 若是在某個可行方案中將 MAXAPPLS設置爲 AUTOMATIC,那麼也應該將 LOCKLIST設置爲 AUTOMATIC。
4. MAXLOCKS配置參數的計算方法以下:
MAXLOCKS = 100 * (512鎖/應用程序 * 32字節/鎖 *2)/(LOCKLIST * 4096字節)
該公式容許任何應用程序持有的鎖是平均數的兩倍。若是隻有幾個應用程序併發地運行,則能夠增大MAXLOCKS,由於在這些條件下鎖列表空間中不會有太多爭用。
5. maxlocks * locklist * 4096 /(100 * 64)(在除HP-UX 環境的 64 位系統上)
4096 是一頁中的字節數,100 是容許 maxlocks 具備的最大百分比值,64 是每一個鎖定的字節數。假設肯定其中一個應用程序須要 1000 個鎖定,而且您不但願發生鎖定升級,那麼應爲該公式中的 maxlocks 和 locklist 選擇值,以便結果大於 1000。對 maxlocks 使用 10 而且對 locklist 使用 100,該公式將產生多於所需的 1000 個鎖定。
6. LOCKLIST值是與 MAXLOCKS參數一塊兒調整的,所以,若是禁用 LOCKLIST參數自調整功能,也將自動禁用 MAXLOCKS參數自調整功能。若是啓用 LOCKLIST參數自調整功能,也將自動啓用 MAXLOCKS參數自調整功能。
7. maxlocks 參數乘以 maxappls 參數不能小於 100。
8. maxlocks = 2 * 100 / maxappls(其中 2 用來完成兩次平均,而 100 表示容許的最大百分比值。)
9. maxlocks = 2 * 100 / (併發運行的應用程序的平均數目) (若是僅有幾個併發運行的應用程序,可用此公式代替。)
鎖升級致使異常問題確認後,如何解決 ?
參照前文所述致使發生鎖升級的發生條件中的描述,顯而易見咱們有以下的方式來儘量的避免鎖升級:
保持 MAXLOCKS 不變,加大 LOCKLIST 的值:DB2 會增長分配給鎖列表的整體內存容量。這樣在單個應用程序可以持有的鎖列表的最大百分比不變的狀況下, 任意一個應用程序在鎖升級前可以持有的鎖的數量都會有所增長。該配置比較適合系統中有多個應用程序都有可能持有大量行鎖的場合。
保持 LOCKLIST 不變,加大 MAXLOCKS 的值:DB2 不會增長分配給鎖列表的整體內存容量,但會增大單個應用程序可以持有的鎖列表的最大百分比。 這樣某個特定的應用程序在鎖升級前可以持有的鎖的數量會有所增長。該配置比較適合系統中只有少數的應用程序有可能持有大量行鎖的場合。
同時加大 LOCKLIST 和 MAXLOCKS 的值:DB2 會同時增長分配給鎖列表的整體內存容量和增大單個應用程序可以持有的鎖列表的最大百分比。 該配置比較適合系統內存容量比較充裕的場合。
因爲系統總體內存容量的限制,不可能無限增大上述參數的值(由於調優了這部分鎖內存相關的參數以後勢必會影響其餘內存相關的設置), 因此須要在一個較爲合理的範圍內控制該參數的取值。篇幅所限,筆者這裏就一點而過,有興趣的讀者能夠自行研究。此外,適當的加大 LOCKTIMEOUT 的設值能夠有效的避免鎖等待而致使的超時現象。 畢竟咱們都不但願有「Error」關鍵字出如今咱們的系統日誌當中。固然 DB2 有本身的回滾機制,不至於會出現業務數據遭到損失的狀況。
5、舉例說明
下面咱們用一個具體的例子來理解:
2017-02-23-14.21.20.342532 Instance:db2inst1 Node:000
PID:253627(db2agent (BASSDB) 0) TID:1 Appid:*LOCAL.db2inst1.0600D1010730
data management sqldEscalateLocks Probe:4 Database: BASSDB
ADM5503E The escalation of "1428392" locks on table "BASS2.ODS_EXTENT_DAILY " to
lock intent "X" has failed. The SQLCODE is "-911".
紅色部份內容說明了有鎖升級發生,可是失敗了,失敗的緣由要看Reason Code ADM5503E, 這裏的狀況是lock timeout, 簡單的解決辦法是增長LOCKTIMEOUT 可是這並很差,若是內存還有剩餘的話仍是增長LOCKLIST比較好一點,固然了,若是沒辦法增長LOCKLIST的話,那就得從程序處着手了:
本來的db參數設置爲:
Max storage for lock list (4KB) (LOCKLIST) = 50000
Percent. of lock lists per application (MAXLOCKS) = 50
Lock timeout (sec) (LOCKTIMEOUT) = 60
對LOCKLIST進行了更改,直接double
Max storage for lock list (4KB) (LOCKLIST) = 100000
Percent. of lock lists per application (MAXLOCKS) = 50
Lock timeout (sec) (LOCKTIMEOUT) = 60
如今沒有了鎖升級的報警了。
對於本來的配置能夠算出這些內存空間最多能夠鎖住多少行的數據:
50000*4K*50%*1024=102400000 Byte
咱們再比對一下官方的文檔(咱們的系統爲64位):
On 32-bit platforms, each lock requires 36 or 72 bytes of the lock list,
depending on whether other locks are held on the object:
On 64-bit platforms, each lock requires 56 or 112 bytes of the lock list,
depending on whether other locks are held on the object:
對於咱們客戶的具體狀況,最多能夠鎖住 102400000/56=1828571 一百多萬行,咱們經過這個計算結果與db2diag.log中的sql語句須要的鎖相比較,沒有超過這個值的鎖升級成功,而在咱們改過參數後——
100000*4K*50%*1024=204800000 Byte
204800000/56=3657142
發現有三百多萬行鎖升級成功的記錄:
2017-02-23-14.35.21.435254 Instance:db2inst1 Node:000
PID:323542(db2agent (BASSDB) 0) TID:1 Appid:*LOCAL.db2inst1.070D92045332
data management sqldEscalateLocks Probe:3 Database:BASSDB
ADM5502 The escalation of "3124245" locks on table "BASS2.ODS_OTHER_DAY" to
lock intent "X" was successful. 其實關於鎖的內容相對較多,理解起來也相對複雜,建議多從實際演練中增長鎖升級的優化經驗,多配置一些監控工具以便對數據的分析。