級別: 中級java
齊克科 (qikeke@cn.ibm.com), 軟件工程師, IBM
Elaine Zhan (ezhan@cn.ibm.com), 軟件諮詢工程師, IBM
陳荔龍 (chenlil@cn.ibm.com), 軟件工程師, IBM數據庫
2008 年 5 月 08 日編程
本文介紹一種數據庫併發機制表格分析方法。藉助表格,可以幫助理解、分析 DB2 的併發機制,並能對 DB2如何實現這種併發機制窺探一二。咱們還將藉助一些實例,來進行概念的驗證和對理論更加深入的理解。
基本概念安全
對於數據庫初學者來講,深入理解 DB2 裏面的併發機制並非一件很容易的事。事務、隔離級別、鎖、鎖兼容這些概念讀起來十分晦澀,這些概念之間的關係彷佛也是若隱若現,使人琢磨不透。服務器
這裏咱們無心把全部的數據庫併發機制相關概念再複述一遍,這不是本文的重點,可是頗有必要在這裏對一些重要的概念稍微複習一下,包括事務,隔離級別,鎖,鎖兼容等。對這些概念有大概的瞭解會對您閱讀本文大有幫助。併發
事務jsp
事 務在 DB2 中一般稱爲工做單元。工做單元是應用程序進程內可恢復的操做序列。數據庫管理器使用它來確保數據庫處於一致狀態。對數據庫的任何讀取或寫入都在一個工做單 元內執行。當對數據庫發出第一條 SQL 語句時,就隱式啓動了一個工做單元。由同一應用程序執行的全部後續讀寫操做被認爲是同一工做單元的一部分,直到應用程序發出 COMMIT 或 ROLLBACK 語句來結束該工做單元。性能
隔離級別學習
隔離級別肯定訪問數據時如何鎖定或者隔離該數據,以使其不受其餘進程的影響。除非顯式地進行更改,不然隔離級別在工做單元的運行期間保持有效。 因爲多個用戶訪問和更改數據,因此必須維護關係數據庫數據完整性。併發性指的是多個交互式用戶或應用程序能夠同時共享資源。隔離級別指定: 應用程序讀取和更新的行可供其餘併發執行的應用程序進程使用的程度;以及應用程序受其餘併發執行的應用程序進程的更新活動的影響程度。優化
DB2 支持下列隔離級別:
要提供併發性控制並防止無控制的數據訪問,數據庫管理器將鎖定置於緩衝池、表、數據分區、表塊或錶行上。鎖定將數據庫管理器資源與應用程序關聯(稱爲鎖定全部者),以控制其餘應用程序訪問同一資源的方式。
鎖的基本屬性包括:object, size, duration, mode。若是您對這些屬性所表達的含義不是特別清楚,建議您先讀一下相關文章和 DB2 infocenter 裏相關章節。咱們在這裏只是羅列出本文所須要的必要的信息。
鎖的持續時間會根據隔離級別而有所不一樣:
鎖狀態(mode)
鎖 兼容:在一個應用程序當前擁有對某個對象的鎖定,而另外一個應用程序請求對同一個對象的鎖定時,就會出現鎖定兼容性問題。當兩種鎖定方式兼容時,能夠贊成對 該對象的第二個鎖定請求。若是請求的鎖定的鎖定方式與已掛起的鎖定不兼容,則不能贊成鎖定請求。相反,請求要等到第一個應用程序釋放其鎖定,而且釋放全部 其餘現有不兼容鎖定爲止。
|
也許讀完上面的一些概念,仍是會有許多疑問,諸如在不一樣的隔離級別下作不一樣的操做前須要知足那些條件,以及操做完成後對其餘事務到底會產生什麼影響?除了從文字上進行推敲,有沒有其餘的辦法幫助咱們分析和判斷 DB2 的並行機制? 如今就讓咱們進入本文主題。本文將介紹一種表格分析法,藉助兩種表格,能夠對使用不一樣隔離級別的事務進行不一樣操做時的行爲進行精確分析和判斷。 這裏咱們要引入兩類特別重要的表,一張表是用來描述使用不一樣隔離級別在作不一樣的操所時須要獲取的鎖類型,另外一張表描述的是鎖類型的兼容性。
事務在不一樣的隔離級別中進行不一樣的操做,獲取的鎖類型也不一樣。好比在最高隔離級別 RR 中修改數據獲取的鎖 (S) 與 CS 級別中讀數據獲取的鎖(NS) 就不同。 另外訪問方案也可對鎖定方式具備很大影響。舉例來講,當使用索引掃描來查找特定行時,優化器將可能爲該表選擇行級別鎖定(IS);而若是不使用索引,則必須按順序掃描整個表來找到所選的行,而且可所以獲取單個表級鎖定(S)。 所以獲取的鎖定的類型依賴於隔離級別、操做類型,以及訪問方案諸多因素。爲了簡單起見,咱們只對標準表,使用索引和所有掃描的訪問方案來作分析。本文沒有涉及集羣表中的鎖定以及被延遲的數據頁訪問方式。
表1描述了在不使用謂詞 ( SQL 語句裏的 where 部分) 進行表掃描的訪問方式下,在不一樣隔離級別下作各類操做所要獲取的鎖定方式。
隔離級別 | 只讀和模糊掃描 | 遊標-掃描 | 遊標-當前行 | 更新或刪除-掃描 | 更新或刪除-更新行 |
---|---|---|---|---|---|
RR | S/-- | U/-- | SIX/X | X/-- | X/-- |
RS | IS/NS | IX/U | IX/X | IX/X | IX/X |
CS | IS/NS | IX/U | IX/X | IX/X | IX/X |
UR | IN/-- | IX/U | IX/X | IX/X | IX/X |
表2描述了在使用謂詞 ( SQL 語句裏的 where 部分) 進行表掃描的訪問方式下,在不一樣隔離級別下作各類操做所要獲取的鎖定方式。
隔離級別 | 只讀和模糊掃描 | 遊標-掃描 | 遊標-當前行 | 更新或刪除-掃描 | 更新或刪除-更新行 |
---|---|---|---|---|---|
RR | S/-- | U/-- | SIX/X | U/-- | SIX/X |
RS | IS/NS | IX/U | IX/X | IX/U | IX/X |
CS | IS/NS | IX/U | IX/X | IX/U | IX/X |
UR | IN/-- | IX/U | IX/X | IX/U | IX/X |
表3描述了在使用具備惟一性的索引來進行索引掃描的訪問方式下,在不一樣隔離級別下作各類操做所要獲取的鎖定方式。
隔離級別 | 只讀和模糊掃描 | 遊標-掃描 | 遊標-當前行 | 更新或刪除-掃描 | 更新或刪除-更新行 |
---|---|---|---|---|---|
RR | IS/S | IX/U | IX/X | IX/X | IX/X |
RS | IS/NS | IX/U | IX/X | IX/X | IX/X |
CS | IS/NS | IX/U | IX/X | IX/X | IX/X |
UR | IN/-- | IX/U | IX/X | IX/X | IX/X |
在上面兩張表中,每一項由兩部分組成: 表鎖定和行鎖定,橫線表明沒有該對象上的鎖定。
表4顯示鎖定之間的兼容關係。Y 表示鎖定兼容,即當一個進程掛起或正在請求對同一個資源的鎖定時,能夠贊成鎖定請求。N 表示鎖定不兼容,請求者必須等待,直到全部不兼容的鎖定被其餘進程釋放爲止。
無 | IN | IS | NS | S | IX | SIX | U | X | Z | NW | W | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
無 | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y |
IN | Y | Y | Y | Y | Y | Y | Y | Y | Y | N | Y | Y |
IS | Y | Y | Y | Y | Y | Y | Y | Y | N | N | N | N |
NS | Y | Y | Y | Y | Y | N | N | Y | N | N | Y | N |
S | Y | Y | Y | Y | Y | N | N | Y | N | N | N | N |
IX | Y | Y | Y | N | N | Y | N | N | N | N | N | N |
SIX | Y | Y | Y | N | N | N | N | N | N | N | N | N |
U | Y | Y | Y | Y | Y | N | N | N | N | N | N | N |
X | Y | Y | N | N | N | N | N | N | N | N | N | N |
Z | Y | N | N | N | N | N | N | N | N | N | N | N |
NW | Y | Y | N | Y | N | N | N | N | N | N | N | Y |
W | Y | Y | N | N | N | N | N | N | N | N | Y | N |
下面咱們經過一個實例來看一個如何根據這些表格來分析 DB2 中的鎖機制。首先須要準備試驗環境。
這裏咱們使用了 DB2 V9,實際上這些試驗一樣適用於 DB2 V8。
首先運行 db2cmd,初始化 DB2 CLP (命令行處理器)的運行環境,而後輸入 db2,進入命令行處理器。建立數據庫,鏈接到數據庫,建立表格,以及插入兩條試驗數據,具體操做如 listing1:
|
然 後咱們使用兩個 CLP 做爲兩個獨立的進程,分別運行事務,來觀察和分析 DB2 中的鎖機制。兩次運行 db2cmd,打開兩個 DB2 的命令運行環境,而後輸入 db2 +c,進入命令行處理器。加參數 +c 是爲了保證在 DB2 CLP 中的 SQL 語句不自動提交,這種方式下,只有輸入命令 commit 或者 rollback 纔會結束一個事務。
在第一個 CLP 中運行
|
咱們設定了事務的隔離級別爲讀穩定 (RS),而且修改了一條數據。注意這裏沒有 commit,由於咱們不想結束這個事務。
在第二個 CLP 中,運行
|
在第二個事務中,咱們會發現可以讀取出 id='2' 的數據,可是當查詢 id='1' 的數據時,沒有數據輸出,說明發生了鎖等待。
下面咱們來分析一下:事務一執行 update 操做時,由於在 where 語句中使用的是name='a'
,沒有包括鍵值,因此數據庫管理器在進行操做時會進行全表掃描。 咱們就應該看使用謂詞的表掃描的鎖定方式表格,從表 2 中能夠判斷,在 RS 級別下,更新的行(被修改的第一行)加 X 鎖,更新-掃描行(第二行)加 U 鎖。
事務二在執行select * from test where id='2'
時,where 語句中使用主鍵值,對於主鍵是建有索引的,所以根據表 3 (惟一匹配的索引掃描鎖定表)來判斷須要獲取的鎖模式)。 從表中能夠看出,RS 級別下被掃描的行(只有第二行)須要申請 NS 鎖。第二行已經被事務一加了 U 鎖,那麼事務二可否獲取到 NS 鎖呢,這就要看 U 鎖和 NS 鎖是否兼容。 這時須要查看鎖的兼容表。從表4中能夠看出,U 和 NS 是兼容的,所以事務二能夠獲取鎖,可以完成查詢操做。
當事務二執行select * from test where id='1'
時,從表 3 中查出,被掃描的第一行須要 NS 鎖,與事務一加的 X 鎖不兼容,所以發生鎖等待。 這時只有第一個事務運行 commit 或者 rollback,事務一結束,X 鎖被釋放,第二個事務才能夠獲取到 NS 鎖,獲得查詢結果。
那麼若是在事務二中運行select * from test where name='b'
呢?這條語句是要查詢的第二條記錄,須要的 NS 鎖與事務一在這條記錄上的U鎖是兼容的,應該不會發生鎖等待,能夠獲得結果。事實是否是這樣呢? 作一下試驗就會發現,也是須要等待的,爲何呢?仔細想一想就會發現,語句select * from test where name='b'
並無使用索引,而是作全表掃描,從 table 1 中能夠獲得,在隔離級別爲 RS 的事務中,全部被掃描的行都須要 NS 鎖,包括第一行,與第一行記錄已經有的 X 鎖是不兼容的,所以當掃描到第一行時就須要鎖等待了。
|
熱身完畢,咱們已經基本瞭解如何使用獲取鎖表和鎖兼容表來分析事務的行爲了。下面咱們針對各個隔離級別分別設計一些試驗來理解隔離級別的意義。
未提交的讀隔離級別 (UR) 是最不嚴格的隔離級別。實際上,在使用這個隔離級別時,僅當另外一個事務試圖刪除或更改被檢索的行所在的表時,纔會鎖定一個事務檢索的行。 由於在使用這種隔離級別時,行一般保持未鎖定狀態,因此髒讀、不可重複的讀和幻像均可能會發生。下面咱們就設計幾個試驗看如何發生的髒讀,不可重複的讀以及幻像。
試驗一:UR下的髒讀
當事務讀取還沒有提交的數據時,就會發生髒讀。例如:事務 1 更改了一行數據,而事務 2 在事務 1 提交更改以前讀取了已更改的行。若是事務 1 回滾該更改,則事務 2 就會讀取被認爲是未曾存在的數據。咱們用試驗來感覺一下。
首先在事務一中執行connect reset; change isolation to UR; connect to testdb
來把事務的隔離級別修改成UR。 第二個事務能夠採用任何隔離級別,只須要保證在第二個窗口中採用不自動提交方式進入 CLP。在第二個事務中運行 update test set name='abc' where id='01'
, 而後在第一個事務中運行 select * from test
。這裏讀取的數據是第二個事務已經修改過的,可是尚未提交的數據。在第二個事務運行 rollback,取消剛纔的修改, 在第一個事務運行 select * from test
,發現讀取的數據又變成了修改前的數據。
圖 1. UR 下的髒讀(事務一)
圖 2. UR 下的髒讀(事務二)
事務一的第一次讀,讀到的就是髒數據。對照表格稍微分析一下就能夠得知,發生髒讀緣由就是 UR 事務讀操做的時候不對行進行的鎖定,這樣一方面事務自己讀數據時不受約束,同時因爲不對數據進行鎖定,那麼使得其餘事務修改時也就不受 UR 事務的約束了。
遊標穩定性隔離級別在隔離事務效果方面很是寬鬆。它能夠防止髒讀;但有可能出現不可重複的讀和幻像。
試驗二:CS 下如何防止髒讀
把事務一的隔離級別改成 CS,而後重複上面的試驗,會發如今事務二修改數據之後,在事務二提交之前,事務一去讀修改的數據時會發生鎖等待,直到事務二 commit 或者 rollback,所以不會發生髒讀。試驗過程以下圖所示:
圖 3. CS 下防止髒讀(事務一)
圖 4. CS 下防止髒讀(事務二)
仍是利用咱們的表格分析法,從表中能夠看出,不管採用什麼訪問方式,在 CS 隔離級別下,讀取數據時須要在行上加 NS 鎖,若是此時數據上己經有其餘事務加了X鎖(被修改時加X鎖),因爲 NS 鎖與 X 鎖的不兼容,使得讀數據的時候必定發生鎖等待。
試驗三:CS 下不可重複的讀
CS 能夠防止髒讀,可是可能出現不可重複的讀和幻像。以下圖試驗所示:事務一讀完數據後,事務二修改數據(或者添加數據),而且提交,那麼事務一再去讀數據, 會發生同一條記錄跟上一次讀的結果不同,這就是不可重複的讀(也會發生讀出上一次沒有查詢到的記錄,這就是幻像)。
圖 5. CS 下不可重複的讀(事務一)
圖 6. CS 下不可重複的讀(事務二)
但 是若是咱們仔細按照表格分析就會產生這樣的疑問:當第一個事務讀取數據之後,按照表格1,數據應該加 NS 鎖,事務二在修改數據的時候,須要在被修改數據行上加X鎖,表 4 中看出,X 鎖與 NS 鎖不兼容,事務二應該是鎖等待阿,然而狀況並不是如此。從試驗中能夠看出,事務二在修改數據時沒有等待。 不過這確實符合 CS 隔離級別的描述,CS 級別下,只讀取已經提交了數據,可是讀取並不會影響其餘事務對數據的修改。可是與表格分析得出的結果矛盾,緣由在哪兒呢? 咱們能夠藉助 DB2 活動監視器來分析一下。咱們先來複現一下事務一讀取數據之後的狀態。首先在經過 commit 來結束事務一,而後從新執行 select 語句以恢復到讀取後的狀態。 打開 DB2 活動監控器,選擇數據庫 testdb,而後在檢視任務中選擇正在解決應用程序鎖定狀態,點擊完"成看"。 在報告欄中選擇"具備最大鎖定超時數的應用程序",找到第一個 CLP 對應的程序,右擊,選擇顯示鎖定鏈,以下圖:
在應用程序鎖定鏈窗口中,右擊程序,選擇顯示鎖定詳細信息。
在詳細信息窗口中,會列出此程序已經擁有的鎖定和等待的鎖定。在這裏面,咱們並無發現針對行對象的 NS 鎖。 咱們分別在 CS 級別和 RS 級別下作相同的查詢操做,而後比較它們的鎖定情況,就會更清楚地發現它們的不一樣,儘管在表 1 和表 2 裏 CS 和 RS 看起來是同樣的。
圖 9. CS 級別鎖定情況
圖 10. RS 級別鎖定情況
什麼緣由呢?是否是表 1 和表 2 把鎖模式弄錯了,CS 隔離級別下讀操做也不須要 NS 鎖?應該也不是,由於若是咱們讓事務二先修改數據,再讓事務一去讀,就回發生鎖等待,所以事務一讀數據時,仍是須要行的 NS 鎖,不然象 UR 級別同樣,也會發生髒讀。 回頭看一下咱們在基本概念一節中的這樣一段話:
鎖的持續時間會根據隔離級別而有所不一樣:
也就是說在只有遊標操做方式的時候,纔會在當前行加鎖,象咱們所作的只讀操做,不會持續佔用鎖,讀操做完成後鎖就會被釋放掉,不會等到事務結束,這樣就能夠解釋在 CS 級別下,爲何對數據的讀取不會阻塞其餘事務對該數據的修改了。
讀穩定性隔離級別:讀穩定性隔離級別能夠防止髒讀和不可重複的讀,可是可能出現幻像。在使用這個隔離級別時,只鎖定事務實際檢索和修改的行。
試驗四:RS 下防止不可重複的讀
將事務一的隔離級別改成 RS,而後執行select * from test where id='1'
讀取數據; 事務二執行update test set name='abc' where id='1'
來修改數據,會發現事務二發生鎖等待。 事實上,不管第二個進程使用何種隔離級別,現象是同樣的。仍是用鎖兼容表分析一下。在事務一讀數據時,由於是索引掃描,所以從表 3 中得出,只是在被檢索的數據上加 NS 鎖(不一樣於 CS,鎖會持續到事務結束), 事務二在作修改的時候,獲取 X 鎖,與 NS 不兼容,所以鎖等待,直至事務一結束。這樣就保證了在事務一的操做過程當中,被檢索的行不會被其餘進程修改,每次讀到的同一條記錄都不會有變化,從而保證了讀穩定。
圖 11. RS 下防止不可重複的讀(事務一)
圖 12. RS 下防止不可重複的讀(事務二)
試驗五:RS 下的幻像
那麼如何發生的幻像呢?事務一讀取數據後,在事務二中插入一條數據insert into test values('3', 'c')
而且提交, 再在事務一中執行select * from test
, 就會發現與前一次 select 結果相比,多了一條數據,這就是幻像。
圖 13. RS 下的幻像(事務一)
圖 14. RS 下的幻像(事務二)
可重複讀隔離級別:可重複讀隔離級別是最嚴格的隔離級別。在使用它時,一個事務的影響徹底與其餘併發事務隔離:髒讀、不可重複的讀、幻像都不會發生。咱們如今就來驗證一下。
試驗六:RR 下防止幻像
事務一隔離級別改成 RR,而後執行select * from test
, 事務二執行insert into test values('4', 'd')
,會發現事務二發生鎖等待。 仍是用表格分析法,事務一讀取數據時,從表1能夠看出,在表上加了 S 鎖,事務二插入數據時須要在表上申請 X 鎖,X 與 S 不兼容,所以進程二隻能鎖等待。
圖 15. RR 下防止幻像(事務一)
圖 16. RR 下防止幻像(事務二)
這樣咱們就會很天然地發現,隔離級別越高,操做越安全,可是發生鎖等待的機會就越大,效率會下降,甚至會發生死鎖。下面咱們再來設計一個死鎖的例子。
兩個進程中的事務都採用 RS (讀穩定)的隔離級別,事務一執行select * from test where id='1'
, 事務二執行select * from test where id='2'
, 事務一執行update test set name='bb' where id='2'
, 事務二執行update test set name='bb' where id='1'
。 這種狀況下,就會發生事務一在執行 update 的時候等待事務二的 NS 鎖被釋放,而事務二執行 update 的時候也會等待事務一的 NS 鎖別釋放,這樣就發生了死鎖。 好在 DB2 能進行死鎖檢測,當發現死鎖時,會中斷並回滾其中一個事務,另外一個事務就能夠繼續了。
圖 17. RR 下防止幻像(事務三)
圖 18. RR 下防止幻像(事務四)
|
咱們已經經過試驗的方法,掌握瞭如何運用表格來分析和判斷事務在不一樣的隔離級別下,執行不一樣的操做時的行爲,從而可以更加深入理解 DB2 的併發機制。 您能夠用這種方法來對您的數據庫併發操做方案進行分析和評估,來判斷是否知足實際需求。可是要指定好的併發操做方案,仍是須要對數據庫的併發機制有着融會貫通的理解。
齊克科 2001年加入IBM,目前就任於中國開發中心,從事MBPS(Managed Business Process Services)相關的工做。 |
Elaine Zhan 中國開發中心 MBPS develope lead,負責CSDP common service 和 provisioning 的開發工做。 |
陳荔龍 2004年加入IBM,目前就任於中國開發中心,從事MBPS(Managed Business Process Services)相關的工做。 |