前言
揭示這種神秘鎖和其他 DB2 for z/OS 難題的真實面目。
SIX 鎖是 DB2 的許多神秘特性之一。它是什麼?如何獲得它?另一個神秘特性是 plan_table 上的 index_only 標志:對於 UPDATE 或 DELETE,為何要設置為 Y?再問一下,UPDATE 或 DELETE 為何一定就是 “index-only”?第三個神秘現象是,在 SQL 語句中添加 ORDER BY 可能消除排序。更奇怪的情況是,刪除 ORDER BY 子句實際上可能在訪問路徑中增加排序。在後面幾篇專欄文章中,我將討論這些神秘現象並嘗試解決它們。現在讓我們當一回少女神探 Nancy Drew,開始探索神秘的 SIX 鎖。
SIX 鎖
在以前的專欄文章(“表空間和鎖級別,第 1 部分”,2005 年第 1 期)中,我通過類比解釋了 DB2 鎖:把建築物比作表空間,把樓層比作表,把房間比作頁。用戶必須先進入建築物,然後進入某一樓層,最後進入房間。同樣,在使用頁級鎖時,無法直接進入頁。必須先進入表空間(相當於建築物),然後進入表(相當於樓層),最後進入頁(相當於房間)。典型的更新者首先請求一個表空間鎖,然後請求表鎖,最後請求頁鎖。外層(即高級)鎖稱為 “Intent Lock(意向鎖)”。如果進入頁,您的意圖便是獲得頁級鎖。例如,可能必須獲得表空間上的 IX 鎖(希望執行某些特定的操作;I 表示 Intend,X 表示 eXclusive),然後獲得表上的 IX 鎖,最後再獲得頁上的 X(exclusive)鎖。
通常情況下,對於執行維護的用戶,需要表空間和表上的 IX 鎖;對於只執行讀操作的用戶,需要 IS(Intend to Share)鎖。但是,對於這兩種情況,在這些級別上都會看到 SIX 鎖。這種鎖是什麼?它有什麼作用?
只能讀,不能進行更新
假設包含我們的表的表空間在定義時使用了 LOCKSIZE PAGE。我有一個程序,它必須在沒有其他人執行維護的情況下運行。我希望這個程序是執行 INSERT、UPDATE、DELETE 和 MERGE 的惟一程序。但是,如果其他程序執行讀操作,並不會影響我的程序。允許 SELECT 和只讀的 CURSOR。如何實現這個場景呢?
我打算這樣做:我將用自己的鎖規則覆蓋表空間的頁級鎖規則。在我的程序的初始化階段,向 DB2 發出 LOCK TABLE mytablename IN SHARE MODE 語句。這個語句讓 DB2 獲得表空間(建築物)上的 IS 鎖和表(樓層)上的 S 鎖。表上的 S 鎖允許其他用戶執行讀操作。實際上,S 鎖允許我與兩種用戶共享數據:使用三級 IS/IS/S 協議只執行讀操作的用戶,以及發出 LOCK TABLE ... IN SHARE MODE 語句的用戶。但是我更願意與前者共享數據。現在停一下,我們來看看是否能夠解決這個問題。
我需要在您執行讀操作時執行維護
請記住,在我的程序運行時,它不但要執行讀操作,還要執行維護。我希望確保別人不會以 SHARE 模式鎖住整個表,否則就無法執行維護。我不能以 EXCLUSIVE 模式鎖住這個表,否則在我的程序運行時別人就無法執行讀操作。這種進退兩難的局面可以通過 SIX 鎖來解決。我們來看看幕後發生的情況。
通過發出 LOCK TABLE 語句,我獲得了表空間上的 IS 鎖和表上的 S 鎖。DB2 現在知道不需要為我對這個表的讀操作獲取頁鎖。因此,只要我的 LOCK TABLE 語句仍然有效,我就沒有任何頁級 S 鎖。但是,我最終會執行第一個維護操作(INSERT)。請記住,我並沒有以 EXCLUSIVE 模式鎖住這個表,僅僅采用了 SHARE 模式。因此,執行維護的 SQL 語句必須在頁級獲得 X 鎖。因為我的程序與其他程序共享數據,所以它必須獲得 X 頁鎖,從而警告其他讀者它們可能會讀取未提交或 “髒的” 數據。
現在,我處於一種奇怪的狀態。我持有表空間上的 IS 鎖、表上的 S 鎖和頁上的 X 鎖。這並不合法。如果我有頁級 X 鎖,就必須設法讓別的程序在表和表空間級上知道這一情況。如果放任不管,別人就可以成功地執行 LOCK TABLE mytablename in SHARE MODE 語句(通過請求 IS/S 鎖,這些鎖與我的 IS/S 鎖共享數據)。如果這個語句成功地執行了,那個用戶就會誤以為目前沒有人在這個表上執行維護。那麼,我應該怎麼做呢?
鎖規則
在這裡應用三個 DB2 鎖規則。規則 1 是,任何人在任何時候在任何實體上只能持有一個鎖。規則 2 是,如果一個用戶需要單一實體上的兩個不同的鎖,那麼獲得的鎖是這兩個鎖中限制較嚴的那個。規則 3 是,每個鎖必須報告可能存在的任何低級鎖。
如果我在持有 IS/S 鎖的情況下讀取一個頁上的第一行,那麼不需要獲得 S 鎖。但是,如果我在同一個工作單元中更新同一頁上的另一行(比如第五行),或者在剛才讀取的同一頁上插入一個新行,那麼會發生什麼情況?這時我需要這個頁上的 X 鎖。根據規則 1,我(一個用戶)在單一實體(在這裡,實體是一個頁)上只能持有一個鎖。根據規則 2,我持有的鎖將是兩個鎖中限制較嚴的那個。X 鎖的限制比 S 鎖嚴得多(換句話說,更不友好);因此,理論上的 S 鎖(“我只執行讀操作”)和 X 鎖(“我要執行維護”)這兩個需求會導致我持有頁上的一個 X 鎖。
到目前為止,根據規則 1 和 2,我持有表空間上的 IS 鎖、表上的 S 鎖和頁上的 X 鎖。但是,還必須實施規則 3。如果只查看高級鎖,在這些級上沒有任何跡象表明我在頁級上持有 X 鎖。在表空間級,IS 鎖表明可能存在低級的 S 鎖,但是它還表明沒有低級的 X 鎖。如果把表空間 IS 鎖升級為 IX 鎖,就向別的用戶表明我不但可能有低級 S 鎖(在這個示例中,是在表級),還可能有低級 X 鎖(在這個示例中,是在頁級)。這樣的話,規則 1、2 和 3 都已經成功地應用於表空間級了。
解決難題
現在,我們來看看表級 S 鎖並應用這些規則。S 鎖向其他用戶表明我的應用程序沒有低級(頁級)S 鎖。表級的 IX 鎖會告訴其他用戶我有低級的 X 頁鎖。我不能在這一個實體上有兩個鎖(規則 1)。因此,必須判斷哪個鎖的限制更嚴(規則 2)。選擇的鎖還必須報告可能存在的任何低級鎖(規則 3)。在這種非常特殊的情況下,S 鎖和 IX 鎖都不合適。它們都不滿足規則 3 的要求。因此,在 S 鎖和 IX 鎖之間 “協商” 的結果是獲得實體上的一個 SIX 鎖。
SIX 鎖的 S 部分告訴其他用戶,我沒有低級(頁級)S 鎖,但是我允許他們用 IS 鎖執行讀操作(我的 S 鎖可以與其他用戶的 IS 鎖共存)。SIX 鎖的 IX 部分告訴其他用戶,我有低級的 X 頁鎖。他們應該知道,如果他們請求這個頁上的 S 頁鎖,就可能遇到超時。
圖 1. SQL 鎖兼容性矩陣
所以,如果看一下 SQL 鎖兼容性矩陣,就會發現我的 SIX 鎖只與 IS 鎖共享數據(圖 1)。這正是我要的效果。
如果其他用戶使用 IS/IS/S 鎖協議在表中進行頁級鎖定,操作會成功。如果有人嘗試在這三級上依次獲得 IX/IX/X 鎖,他會在表級被拒絕。如果有人嘗試在前兩級上發出 LOCK TABLE 語句並(以 eXclusive 模式)獲得 IX/X 鎖或(以 Share 模式)IS/S 鎖,也會在表級被拒絕。通過使用 IX/SIX/X 協議,我的應用程序可以在不獲得頁鎖的情況下執行讀操作,還允許其他用戶用 IS/IS/S 協議執行讀操作,而且只在執行維護期間有頁鎖開銷。
解釋了一個神秘現象
我已經解釋了 DB2 中的一個神秘現象。在我的下一篇專欄文章中,我們將再次扮演偵探,看看我們是否能夠解開其他迷團。