資料庫事務管理原理
資料庫是一個共享資源,可以提供多個使用者使用。這些使用者程式可以一個一個地序列執行,每個時刻只有一個使用者程式執行,執行對資料庫的存取,其他使用者程式必須等到這個使用者程式結束以後方能對資料庫存取。但是如果一個使用者程式涉及大量資料的輸入/輸出交換,則資料庫系統的大部分時間處於閒置狀態。因此,為了充分利用資料庫資源,發揮資料庫共享資源的特點,應該允許多個使用者並行地存取資料庫。但這樣就會產生多個使用者程式併發存取同一資料的情況,若對併發操作不加控制就可能會存取和儲存不正確的資料,破壞資料庫的一致性,所以資料庫管理系統必須提供併發控制機制。併發控制機制的好壞是衡量一個數據庫管理系統效能的重要標誌之一。
DM用封鎖機制來解決併發問題。它可以保證任何時候都可以有多個正在執行的使用者程式,但是所有使用者程式都在彼此完全隔離的環境中執行。
一、 併發控制的預備知識
(一) 併發控制概述
併發控制是以事務(transaction)為單位進行的。
1. 併發控制的單位――事務
事務是資料庫的邏輯工作單位,它是使用者定義的一組操作序列。一個事務可以是一組SQL語句、一條SQL語句或整個程式。
事務的開始和結束都可以由使用者顯示的控制,如果使用者沒有顯式地定義事務,則由資料庫系統按預設規定自動劃分事務。
事務應該具有4種屬性:原子性、一致性、隔離性和永續性。
(1)原子性
事務的原子性保證事務包含的一組更新操作是原子不可分的,也就是說這些操作是一個整體,對資料庫而言全做或者全不做,不能部分的完成。這一性質即使在系統崩潰之後仍能得到保證,在系統崩潰之後將進行資料庫恢復,用來恢復和撤銷系統崩潰處於活動狀態的事務對資料庫的影響,從而保證事務的原子性。系統對磁碟上的任何實際資料的修改之前都會將修改操作資訊本身的資訊記錄到磁碟上。當發生崩潰時,系統能根據這些操作記錄當時該事務處於何種狀態,以此確定是撤銷該事務所做出的所有修改操作,還是將修改的操作重新執行。
(2)一致性
一致性要求事務執行完成後,將資料庫從一個一致狀態轉變到另一個一致狀態。它是一種以一致性規則為基礎的邏輯屬性,例如在轉賬的操作中,各賬戶金額必須平衡,這一條規則對於程式設計師而言是一個強制的規定,由此可見,一致性與原子性是密切相關的。事務的一致性屬性要求事務在併發執行的情況下事務的一致性仍然滿足。它在邏輯上不是獨立的,它由事務的隔離性來表示。
(3) 隔離性
隔離性意味著一個事務的執行不能被其他事務干擾。即一個事務內部的操作及使用的資料對併發的其他事務是隔離的,併發執行的各個事務之間不能互相干擾。它要求即使有多個事務併發執行,看上去每個成功事務按序列排程執行一樣。這一性質的另一種稱法為可序列性,也就是說系統允許的任何交錯操作排程等價於一個序列排程。序列排程的意思是每次排程一個事務,在一個事務的所有操作沒有結束之前,另外的事務操作不能開始。由於效能原因,我們需要進行交錯操作的排程,但我們也希望這些交錯操作的排程的效果和某一個序列排程是一致的。 DM實現該機制是通過對事務的資料訪問物件加適當的鎖,從而排斥其他的事務對同一資料庫物件的併發操作。
(4)永續性
系統提供的永續性保證要求一旦事務提交,那麼對資料庫所做的修改將是持久的,無論發生何種機器和系統故障都不應該對其有任何影響。例如,自動櫃員機( ATM)在向客戶支付一筆錢時,就不用擔心丟失客戶的取款記錄。事務的永續性保證事務對資料庫的影響是持久的,即使系統崩潰。正如在講原子性時所提到的那樣,系統通過做記錄來提供這一保證。
DM沒有提供顯式定義事務開始的語句,第一個可執行的SQL語句(除CONNECT語句外)隱含事務的開始,但事務的結束可以由使用者顯式的控制。在DM中以下幾種情況都結束 (正常,非正常)某一事務:
(1)當某一連線的屬性設定為自動提交,每執行一條語句都會提交;
(2)遇到COMMIT/ROLLBACK語句,便提交/回滾一事務;
(3)當系統的 DDL自動提交開關開啟時(預設為開啟),遇到DDL語句則自動提交該DDL語句和以前的DML和DDL操作;
(4)事務所在的程式正常結束和使用者退出;
(5)系統非正常終止時;
說明:DM在配置檔案中提供了DDL語句的自動提交開關DDL_AUTO_COMMIT。 當此配置項的值為 1(預設情況)時,所有DDL語句自動提交;當此配置項的值為0時,除CREATEDATABASE、ALTERDATABASE和CREATESCHEMA語句外的所有DDL語句都不自動提交。
DM中的一致性是以事務為基礎的。DM通過提交和回滾分別用於將對資料庫的修改永久化和廢除,但是無論是提交和回滾,DM保證資料庫在每個事務開始前、結束後是一致的。為了提高事務管理的靈活性,DM提供了設定儲存點(SAVEPOINT)語句和回滾到儲存點語句。儲存點提供了一種靈活的回滾,事務在執行中可以回滾到某個儲存點,在該儲存點以前的操作有效,而以後的操作被回滾掉。
DM中的事務同樣具有上述4個屬性:原子性、一致性、隔離性和永續性。
2. 併發操作與資料的不一致性
如果沒有鎖定且多個使用者同時訪問一個數據庫,則當他們的事務同時使用相同的資料時可能會發生問題,導致資料庫中的資料的不一致性。
一個最常見的併發操作的例子是火車/飛機訂票系統中的訂票操作。例如,在該系統中的一個活動序列:
① 甲售票員讀出某航班的機票張數餘額A,設A=16;
② 乙售票員讀出同一航班的機票張數餘額A,也是16;
③ 甲售票員賣出一張機票,修改機票張數餘額A=A-1=15,把A寫回資料庫;
④ 乙售票員也賣出一張機票,修改機票張數餘額A=A-1=15,把A寫回資料庫。
結果明明賣出兩張機票,資料庫中機票餘額只減少1。
這種情況稱為資料庫的不一致性。這種不一致性是由甲、乙兩個售票員併發操作引起的。在併發操作情況下,對甲、乙兩個事務操作序列的排程是隨機的。若按上面的排程序列行,甲事務的修改就被丟失。這是由於第4步中乙事務修改A並寫回覆蓋了甲事務的修改。
併發操作帶來的資料庫不一致性可以分為四類:丟失或覆蓋更新、髒讀、不可重複讀和幻像讀,上例只是併發問題的一種。
<!–[if !supportLists]–> (1) <!–[endif]–> 丟失或覆蓋更新( lost update )
當兩個或多個事務選擇同一資料,並且基於最初選定的值更新該資料時,會發生丟失更新問題。每個事務都不知道其它事務的存在。最後的更新將重寫由其它事務所做的更新,這將導致資料丟失。上面預定飛機票的例子就屬於這種併發問題。事務 1與事務 2先後讀入同一資料 A=16,事務 1執行 A-1,並將結果 A=15寫回,事務 2執行 A-1,並將結果 A=15寫回。事務 2提交的結果覆蓋了事務 1對資料庫的修改,從而使事務 1對資料庫的修改丟失了。
(2) 髒讀
一個事務讀取了另一個未提交的並行事務寫的資料。當第二個事務選擇其它事務正在更新的行時,會發生未確認的相關性問題。第二個事務正在讀取的資料還沒有確認並且可能由更新此行的事務所更改。換句話說,當事務1修改某一資料,並將其寫回磁碟,事務2讀取同一資料後,事務1由於某種原因被撤銷,這時事務1已修改過的資料恢復原值,事務2讀到的資料就與資料庫中的資料不一致,是不正確的資料,稱為髒讀。
例如,在下圖中,事務1將C值修改為200,事務2讀到C為200,而事務1由於某種原因撤銷,其修改作廢,C恢復原值100,這時事務2讀到的就是不正確的“髒“資料了。
(3) 不可重複讀(nonrepeatable read)
一個事務重新讀取前面讀取過的資料,發現該資料已經被另一個已提交的事務修改過。即事務1讀取某一資料後,事務2對其做了修改,當事務1再次讀資料時,得到的與第一次不同的值。
例如,在下圖中,事務1讀取B=100進行運算,事務2讀取同一資料B,對其進行修改後將B=200寫回資料庫。事務1為了對讀取值校對重讀B,B已為200,與第一次讀取值不一致。
(4) 幻像讀
如果一個事務在提交查詢結果之前,另一個事務可以更改該結果,就會發生這種情況。這句話也可以這樣解釋,事務1按一定條件從資料庫中讀取某些資料記錄後未提交查詢結果,事務2刪除了其中部分記錄,事務1再次按相同條件讀取資料時,發現某些記錄神祕地消失了;或者事務1按一定條件從資料庫中讀取某些資料記錄後未提交查詢結果,事務2插入了一些記錄,當事務1再次按相同條件讀取資料時,發現多了一些記錄。
產生上述四類資料不一致性的主要原因是併發操作破壞了事務的隔離性。併發控制就是要用正確的方式排程併發操作,使一個使用者事務的執行不受其他事務的干擾,從而避免造成資料的不一致性。
3. 併發場景列舉
結合SQL語句,列舉各種併發情況(包括可能導致資料不一致性和對資料一致性不產生影響的情況)。A表示某一條資料,b和c都表示滿足某一個標準的兩條或多條資料,^表示“非”的意思,∈表示屬於或包含於的意思,1表示第一個事務,2表示第二個事務。
(二) 併發操作的排程
計算機系統對並行事務中並行操作的排程是隨機的,而不同的排程可能會產生不同的結果,那麼哪個結果是正確的,哪個是不正確的呢?
如果一個事務執行過程中沒有其他事務在同時執行,也就是說沒有受到其他事務的干擾,那麼就可能認為該事務的執行結果是正常的或者預想的,因此將所有事務序列起來的排程策略是正確的排程策略。雖然以不同的順序序列執行事務也可能會產生不同的結果,但由於不會將資料庫置於不一致狀態,所以都可以認為是正確的。由此可以得到如下結論:幾個事務的並行執行是正確的,當且僅當其結果與按某一次序序列地執行它們的結果相同。我們稱這種並行排程策略為可序列化(serializable)的排程。可序列性(serializability)是並行事務正確性的唯一準則。
例如,現在有兩個事務,分別包含下列操作:
事務1:讀B;A=B+1;寫回A;
事務2:讀A;B=A+1;寫回B;
假設A的初值為10,B的初值為2。下圖給出了對這兩個事務的三種不同的排程策略,(a)和(b)為兩種不同的序列排程策略,雖然執行結果不同,但他們都是正確的排程。(c)中兩個事務是交錯執行的,由於執行結果與(a)、(b)的結果都不同,所以是錯誤的排程。(d)中的兩個事務也是交錯執行的,由於執行結果與序列排程1(圖(a))的執行結果相同,所以是正確的排程。
為了保證並行操作的正確性,DBMS的並行控制機制必須提供一定的手段來保證排程是可序列化的。
從理論上講,在某一事務執行時禁止其他事務執行的排程策略一定是可序列化的排程,這也是最簡單的排程策略,但這種方法實際上是不可行的,因為它使使用者不能充分共享資料庫資源。
目前DBMS普遍採用封鎖方法(悲觀方法,DM採用的就是這種方法,SQL Server也是採用的這種方法)來保證排程的正確性;即保證並行操作排程的可序列性。除此之外還有其他一些方法,如時標方法、樂觀方法等。
• 悲觀併發控制
鎖定系統阻止使用者以影響其它使用者的方式修改資料。如果使用者執行的操作導致應用了某個鎖,則直到這個鎖的所有者釋放該鎖,其它使用者才能執行與該鎖衝突的操作。該方法主要用在資料爭奪激烈的環境中,以及出現併發衝突時用鎖保護資料的成本比回滾事務的成本低的環境中,因此稱該方法為悲觀併發控制。
• 樂觀併發控制
在樂觀併發控制中,使用者讀資料時不鎖定資料。在執行更新時,系統進行檢查,檢視另一個使用者讀過資料後是否更改了資料。如果另一個使用者更新了資料,將產生一個錯誤。一般情況下,接收錯誤資訊的使用者將回滾事務並重新開始。該方法主要用在資料爭奪少的環境內,以及偶爾回滾事務的成本超過讀資料時鎖定資料的成本的環境內,因此稱該方法為樂觀併發控制。
• 時標併發控制
時標和封鎖技術之間的基本區別是封鎖是使一組事務的併發執行(即交叉執行)同步,使用它等價於這些事務的某一序列操作;時標法也是使用一組事務的交叉執行同步,但是使它等價於這些事務的一個特定的序列執行,即由時標的時序所確定的一個執行。如果發生衝突,是通過撤銷並重新啟動一個事務解決的。事務重新啟動,則賦予新的時標。
(三) 封鎖
封鎖是事項併發控制的一個非常重要的技術。所謂封鎖就是事務T在對某個資料物件,例如,在標、記錄等操作之前,先向系統發出請求,對其加鎖。加鎖後事務T就對資料庫物件有了一定的控制,在事務T釋放它的鎖之前,其他事務不能更新此資料物件。
1. 封鎖型別
DBMS通常提供了多種資料型別的封鎖。一個事務對某個資料物件加鎖後究竟擁有什麼樣的控制是由封鎖型別決定的。基本的封鎖型別有兩種:排他鎖(exclusive lock,簡記為X鎖)和共享鎖(share lock簡記為S鎖)
排他鎖又稱為寫鎖。若事務T對資料物件A加上X鎖,則只允許T讀取和修改A,其他任何事務都不能再對A加任何型別的鎖,直到T釋放A上的鎖。這就保證了其他事務在T釋放A上的鎖之前不能再讀取和修改A。
共享鎖又稱為讀鎖。若事務T對資料物件A加上S鎖,則其他事務只能再對A加S鎖,而不能加X鎖,直到T釋放A上的鎖。這就保證了其他事務可以讀A,但在T釋放A上的S鎖之前不能對A做任何修改。
排他鎖與共享鎖的控制方式可以用下圖的相容矩陣來表示。
在下圖的封鎖型別相容矩陣中,最左邊一列表示事務T1已經獲得的資料物件上的鎖的型別,其中橫線表示沒有加鎖。最上面一行表示另一事務T2對同一資料物件發出的封鎖請求。T2的封鎖請求能否被滿足用Y和N表示,其中Y表示事務T2的封鎖要求與T1已持有的鎖相容,封鎖請求可以滿足。N表示T2的封鎖請求與T1已持有的鎖衝突,T2請求被拒絕。
2. 封鎖粒度
X鎖和S鎖都是加在某一個數據物件上的。封鎖的物件可以是邏輯單元,也可以是物理單元。例如,在關係資料庫中,封鎖物件可以是屬性值、屬性值集合、元組、關係、索引項、整個索引、整個資料庫等邏輯單元;也可以是頁(資料頁或索引頁)、塊等物理單元。封鎖物件可以很大,比如對整個資料庫加鎖,也可以很小,比如只對某個屬性值加鎖。封鎖物件的大小稱為封鎖的粒度(granularity)。
封鎖粒度與系統的併發度和併發控制的開銷密切相關。封鎖的粒度越大,系統中能夠被封鎖的物件就越小,併發度也就越小,但同時系統開銷也越小;相反,封鎖的粒度越小,併發度越高,但系統開銷也就越大。
因此,如果在一個系統中同時存在不同大小的封鎖單元供不同的事務選擇使用是比較理想的。而選擇封鎖粒度時必須同時考慮封鎖機構和併發度兩個因素,對系統開銷與併發度進行權衡,以求得最優的效果。一般說來,需要處理大量元組的使用者事務可以以關係為封鎖單元;需要處理多個關係的大量元組的使用者事務可以以資料庫為封鎖單位;而對於一個處理少量元組的使用者事務,可以以元組為封鎖單位以提高併發度。
3. 封鎖協議
封鎖的目的是為了保證能夠正確地排程併發操作。為此,在運用X鎖和S鎖這兩種基本封鎖,對一定粒度的資料物件加鎖時,還需要約定一些規則,例如,應何時申請X鎖或S鎖、持鎖時間、何時釋放等。我們稱這些規則為封鎖協議(locking protocol)。對封鎖方式規定不同的規則,就形成了各種不同的封鎖協議,它們分別在不同的程度上為併發操作的正確排程提供一定的保證。本節介紹保證資料一致性的三級封鎖協議和保證並行排程可序列性的兩段鎖協議,下一節將介紹避免死鎖的封鎖協議。
(5) 保證資料一致性的封鎖協議――三級封鎖協議
對併發操作的不正確排程可能會帶來四種資料不一致性:丟失或覆蓋更新、髒讀、不可重複讀和幻想讀。三級封鎖協議分別在不同程度上解決了這一問題。
① 1級封鎖協議
1級封鎖協議的內容是:事務T在修改資料R之前必須先對其加X鎖,直到事務結束才釋放。事務結束包括正常結束(commit)和非正常結束(rollback)。
1級封鎖協議可以防止丟失或覆蓋更新,並保證事務T是可以恢復的。例如,下圖使用1級封鎖協議解決了定飛機票例子的丟失更新問題。
上圖中,事務1在讀A進行修改之前先對A加X鎖,當事務2再請求對A加X鎖時被拒絕,只能等事務1釋放A上的鎖。事務1修改值A=15寫回磁碟,釋放A上的X鎖後,事務2獲得對A的X鎖,這時他讀到的A已經是事務1更新過的值15,再按此新的A值進行運算,並將結果值A=14回到磁碟。這樣就避免了丟失事務1的更新。
在1級封鎖協議中,如果僅僅是讀資料不對其進行修改,是不需要加鎖的,所以它不能保證可重複讀和髒讀。
② 2級封鎖協議
2級封鎖協議的內容是:1級封鎖協議加上事務T在讀取資料R之前必須先對其加S鎖,讀完後即可釋放S鎖。
2級封鎖協議除防止了丟失或覆蓋更新,還可進一步防止髒讀。例如,下圖使用2級封鎖協議解決了髒讀的問題。
|
下圖中,事務1在對C進行修改之前,先對C加X鎖,修改其值後寫回磁碟。這時事務2請求C加上S鎖,因T1已在C上加了X鎖,事務2只能等待事務1釋放它。之後事務1因某種原因被撤銷,C恢復為原值100,並釋放C上的X鎖。事務2獲得C上的S鎖,讀C=100。這就避免了事務2髒讀資料。
在2級封鎖協議中,由於讀完資料後即可釋放S鎖,所以它不能保證可重複讀。
③ 3級封鎖協議
3級封鎖協議的內容是:1級封鎖協議加上事務T在讀取資料之前必須先對其加S鎖,直到事務結束才釋放。
3級封鎖協議除防止丟失或覆蓋更新和不髒讀資料外,還進一步防止了不可重複讀和幻想讀。例如下圖,使用3級封鎖協議解決了不可重複讀和幻像讀問題。
上圖中,事務1在讀A,B之前,先對A,B加S鎖,這樣其他事務只能再對A,B加S鎖,而不能加X鎖,即其他事務只能讀A,B,而不能修改它們。所以當事務2為修改B而申請對B的X鎖時被拒絕,使其他無法執行修改操作,只能等待事務1釋放B上的鎖。接著事務1為驗算再讀A,B,這時讀出的B仍是100,求和結果仍為150,即可重複讀。
上述三級協議的主要區別在於什麼操作需要申請封鎖以及何時釋放鎖(即持鎖時間)。三級封鎖協議可以總結為下表。
(6) 保證並行排程可序列性的封鎖協議――兩段封鎖協議
可序列性是並行排程正確性的唯一準則,兩段鎖(two-phase locking,簡稱2PL)協議是為保證並行排程可序列性而提供的封鎖協議。
兩段封鎖協議規定:
①在對任何資料進行讀、寫操作之前,事務首先要獲得對該資料的封鎖,而且②在釋放一個封鎖之後,事務不再獲得任何其他封鎖。
所謂“兩段”鎖的含義是,事務分為兩個階段,第一階段是獲得封鎖,也稱為擴充套件階段,第二階段是釋放封鎖,也稱為收縮階段。
例如,事務1的封鎖序列是:
Slock A… Slock B… Xlock C… Unlock B… Unlock A… Unlock C;
事務2的封鎖序列是:
Slock A… Unlock A… Slock B… Xlock C… Unlock C… Unlock B;
則事務1遵守兩段封鎖協議,而事務2不遵守兩段封鎖協議。
可以證明,若並行執行的所有事務均遵守兩段鎖協議,則對這些事務的所有並行排程策略都是可序列化的。因此我們得出如下結論:所有遵守兩段鎖協議的事務,其並行的結果一定是正確的。
需要說明的是,事務遵守兩段鎖協議是可序列化排程的充分條件,而不是必要條件。即可序列化的排程中,不一定所有事務都必須符合兩段封鎖協議。例如,在下圖中,(a)和(b)都是可序列化的排程,但(a)遵守兩段鎖協議,(b)不遵守兩段鎖協議。
4. 死鎖和活鎖
封鎖技術可以有效地解決並行操作的一致性問題,但也帶來一些新的問題,即死鎖和活鎖的問題。
(1) 活鎖
如果事務T1封鎖了資料物件R後,事務T2也請求封鎖R,於是T2等待。接著T3也請求封鎖R。T1釋放R上的鎖後,系統首先批准了T3的請求,T2只得繼續等待。接著T4也請求封鎖R,T3釋放R上的鎖後,系統又批准了T4的請求……,T2有可能就這樣永遠等待下去。這就是活鎖的情形,如下圖所示。
避免活鎖的簡單方法是採用先來先服務的策略。當多個事務請求封鎖同一資料物件時,封鎖子系統按請求封鎖的先後次序對這些事務排隊,該資料物件上的鎖一旦釋放,首先批准申請佇列中第一個事務獲得鎖。
(2) 死鎖
如果事務T1封鎖了資料A,事務T2封鎖了資料B。之後T1又申請封鎖資料B,因T2已封鎖了B,於是T1等待T2釋放B上的鎖。接著T2又申請封鎖A,因T1已封鎖了A,T2也只能等待T1釋放A上的鎖。這樣就出現了T1在等待T2,而T2又在等待T1的局面,T1和T2兩個事務永遠不能結束,形成死鎖。如下圖所示。
死鎖問題在作業系統和一般並行處理中已做了深入研究,但資料庫系統有其自己的特點,作業系統中解決死鎖的方法並不一定合適資料庫系統。
目前在資料庫中解決死鎖問題主要有兩類方法,一類方法是採取一定措施來預防死鎖的發生,另一類方法是允許發生死鎖,採用一定手段定期診斷系統中有無死鎖,若有則解除之。
① 死鎖的預防
在資料庫系統中,產生死鎖的原因是兩個或多個事務都已封鎖了一些資料物件,然後又都請求對已為其他事務封鎖的資料物件加鎖,從而出現死鎖等待。防止死鎖的發生其實就是要破壞產生死鎖的條件。預防死鎖通常有兩種方法。
◆ 一次封鎖法
一次封鎖法要求每個事務必須一次將所有要使用的資料全部加鎖,否則就不能繼續執行。例如,在上圖的例子中,如果事務T1將資料物件A和B一次加鎖,T1就可以執行下去,而T2等待。T1執行完後釋放A,B上的鎖,T2繼續執行。這樣就不會發生死鎖。
一次封鎖法雖然可以有效地防止死鎖的發生,但也存在問題。第一,一次就將以後要用到的全部資料加鎖,勢必擴大了封鎖的範圍,從而降低了系統的併發度。第二,資料庫中資料是不斷變化的,原來不要求封鎖的資料,在執行過程中可能會變成封鎖物件,所以很難實現精確地確定每個事務所要封鎖的資料物件,只能採取擴大封鎖範圍,將事務在執行過程中可能要封鎖的資料物件全部加鎖,這就進一步降低了併發度。
◆ 順序封鎖法
順序封鎖法是預先對資料物件規定一個封鎖順序,所有事務都按這個順序執行封鎖。在上例中,我們規定封鎖順是A,B,T1和T2都按此順序封鎖,即T2也必須先封鎖A。當T2請求A的封鎖時,由於T1已經封鎖住A,T2就只能等待。T1釋放A,B上的鎖後,T2繼續執行。這樣就不會發生死鎖。
順序封鎖法同樣可以有效地防止死鎖,但也同樣存在問題。第一,資料庫系統中可封鎖的資料物件及其眾多,並且隨資料的插入、刪除等操作而不斷地變化,要維護這樣極多而且變化的資源的封鎖順序非常困難,成本很高。
第二,事務的封鎖請求可以隨著事務的執行而動態地決定,很難事先確定每一個事務要封鎖哪些物件,因此也就很難按規定的順序取施加封鎖。例如,規定資料物件的封鎖順序為A,B,C,D,E。事務T3起初要求封鎖資料物件B,C,E,但當它封鎖B,C後,才發現還需要封鎖A,這樣就破壞了封鎖順序。
可見,在作業系統中廣為採用的預防死鎖的策略並不很適合資料庫的特點,因此DBMS在解決死鎖的問題上更普遍採用的是診斷並解除死鎖的方法。
② 死鎖的診斷與解除
資料庫系統中診斷死鎖的方法與作業系統類似,即使用一個事務等待圖,它動態地反映所有事務的等待狀況。併發控制子系統週期性地(比如每隔1分鐘)檢測事務等待圖,如果發現圖中存在迴路,則表示系統中出現了死鎖。關於診斷死鎖的詳細討論請參閱作業系統的有關書籍。
DBMS的併發控制子系統一旦檢測到系統中存在死鎖,就要設法解除。通常採用的方法是選擇一個處理死鎖代價最小的事務,將其撤銷,釋放此事務持有的所有的鎖,使其他事務能繼續執行下去。
二、 DM的併發控制
(一) 事務隔離級
事務的隔離級描述了給定事務的行為對其它併發執行事務的暴露程度。 SQL-92共規定了四種隔離級別,通過選擇四個隔離級中的一個,使用者能增加對其它未提交事務的暴露程度,獲得更高的併發度。隔離級別是一個事務必須與其它事務進行隔離的程度。
SQL-92的四種隔離級別如下所示,DM支援所有這些隔離級別:
(1)髒讀(READ UNCOMMITTED):事務隔離的最低級別,事務可能查詢到其它事務未提交的資料, 僅可保證不讀取物理損壞的資料)。
(2)讀提交(READ COMMITTED):DM預設級別,保證不讀髒資料。
(3)可重複讀(REPEATABLE READ):保證不可重複讀,但有可能讀入幻像資料。
(4)可序列化(SERIALIZABLE):事務隔離的最高級別,事務之間完全隔離。
DM允許使用者改變未啟動的事務的隔離級和讀寫特性 ,而且設定的選項將一直對那個連線保持有效,直到顯式更改該選項為止。設定事務隔離級別雖然使程式設計師承擔了某些完整性問題所帶來的風險,但可以換取對資料更大的併發訪問權。與以前的隔離級別相比,每個隔離級別都提供了更大的隔離性,但這是通過在更長的時間內佔用更多限制鎖換來的。DM還提供設定事務只讀屬性的語句,使用該語句後該事務只能做查詢操作,不能更新資料庫。
需要注意的是,事務的隔離級別並不影響事務檢視本身對資料的修改,也就是說,事務總可以檢視自己對資料的修改。事務的隔離級別需要根據實際需要設定,較低的隔離級別可以增加併發,但代價是降低資料的正確性。相反,較高的隔離級別可以確保資料的正確性,但可能對併發產生負面影響。應用程式要求的隔離級別確定了DM使用的鎖定行為。
下表中列出四種隔離級別允許不同型別的現象
注意:丟失或覆蓋更新在所有的標準SQL隔離級中都是禁止的。
(二) 併發處理
1. 資料鎖定機制
DM用資料鎖定機制來解決併發問題。它可以保證任何時候都可以有多個正在執行的事務,但是所有事務都在彼此完全隔離的環境中執行。
DM的封鎖物件為表和元組。封鎖的實施有自動和手動兩種,即隱式上鎖和顯式上鎖。隱式封鎖動作的封鎖根據事務的隔離級有所不同。同時, DM提供給使用者4種手動上鎖語句,用以適應使用者定義的應用系統。
一般而言, DM的隱式封鎖足以保證資料的一致性,但使用者可以根據自己的需要改變對錶的封鎖。 DM提供給使用者四種表鎖:意向共享鎖(IS:INTENSIVE SHARE)、共享鎖(S:SHARE)、意向排它鎖(IX:INTENSIVE EXCLUSIVE)和排它鎖(X:EXCLUSIVE)。例如,在讀提交隔離級下,系統預設的表鎖是 IS或IX ,在這兩種表鎖下,在訪問元組前還需對元組進行封鎖,為了提高系統的效率,使用者可以手動對錶進行 X封鎖,這樣,就不需對訪問元組封鎖。
封鎖機制要達到以下目的:
(1)一致性:保證使用者正在檢視時,改變的資料並未從根本上發生變化。
(2)完整性:保證資料庫的基本結構以正確的順序,準確地反映對它們的所有改變。
一個“ 鎖定” 可以認為是當某一程序需要防止其它程序做某事時獲得的某種東西,當該程序不再關心此事時就 “釋放 ”此鎖定,通常一個鎖定是加在某個 “資源 ”(某些客體,如表 )上的。
DM的內部鎖定是自動完成的。當某一程序要檢視一個客體但不允許其他人修改它時,就獲得一個共享方式的鎖定。當某一程序要修改一客體,並且防止任何其它程序修改它時,就獲得更新方式的鎖定。當某一程序要修改一客體,並且防止任何其它程序修改它或以共享方式封鎖它時,就獲得獨佔方式的鎖定。
2. 鎖定型別
DM中的鎖有三種,表鎖、行鎖和鍵範圍鎖。
◆ 表鎖
表鎖用來封鎖表物件,在對錶進行檢索和更新時,DM會對錶物件進行封鎖,但是DM為使用者提供手動的表鎖語句,使用者可以根據自己的需要改變對錶的封鎖型別。表鎖的模式:意向共享鎖 IS,意向排它鎖 IX,共享鎖 S,排它鎖 X,共四種,其相容矩陣可定義如下表。
◆ 行鎖
行鎖封鎖元組,在存取元組和更新元組前, DM會對元組上行鎖,系統不提供手動的行封鎖語句。行鎖有兩種模式:共享鎖(S)、排它鎖(X),其相容矩陣定義如下表。
◆ 鍵範圍鎖
鍵範圍鎖用在可序列事務上,主要解決了幻像讀併發問題。鍵範圍鎖覆蓋單個記錄以及記錄之間的範圍,可以防止對事務訪問的記錄集進行幻像插入或刪除。鍵範圍鎖僅用於在可序列隔離級別上操作的事務。
可序列性要求,如果任意一個查詢在一個事務中後面的某一時刻再次執行,其所獲取的行集應與該查詢在同一事務中以前執行時所獲得的行集相同。如果本查詢試圖提取的行不存在,則在試圖訪問該行的事務完成之前,其它事務不能插入該行。如果允許另一個事務插入該行,則它將以幻像出現。
如果另一個事務試圖插入駐留在鎖定資料頁上的行,頁級鎖定可以防止新增幻像行,並維護可序列性。但是,如果該行要新增到未被第一個事務鎖定的資料頁,應設定鎖定機制防止新增該行。
鍵範圍鎖通過覆蓋索引行和索引行之間的範圍來工作(而不是鎖定整個基礎表的行)。因為第二個事務在該範圍內進行任何行插入、更新或刪除操作時均需要修改索引,而鍵範圍鎖覆蓋了索引項,所以在第一個事務完成之前會阻塞第二個事務的進行。
鍵範圍鎖由系統自行執行,執行的條件是: (1) 事務隔離級為可序列化級; (2) 查詢結果通過某個索引得出。
使用者上鎖成功後鎖將一直有效,直到當前事務結束時,該鎖被系統自動解除。
3. 鎖定型別比較
4. SQL語句鎖定分析
DM對各種 DDL和GRANT 等非DML 語句都分解為增、刪、改。下表為DM對各種DML語句和查詢語句的封鎖策略。
表: SQL語句封鎖策略
注:S* 表示瞬時鎖,在語句結束後釋放;Range表示鍵範圍鎖。
上表只是系統在一般情況下的處理,當系統檢測到有鎖升級的可能,則會升級鎖。一般而言,IS鎖升級為 S鎖,IX鎖升級為 X鎖,同時,不再進行行封鎖。
5. 自定義鎖定提高系統效率
DM也提供了兩個函式 SET_TABLE_OPTION([db.][sch.]tablename, option, value) 、SET_INDEX_OPTION([db.]indexname, option, value)(具體語法參見《 DM_SQL語言使用手冊》第 8 章)供使用者自行定義鎖定型別,以增強系統併發度,提高系統效率。這兩個函式是為那些清楚地知道特定型別的鎖適用於何種情況的專家級使用者提供的。
函式SET_TABLE_OPTION() 用於禁用指定表上的頁級鎖、行級鎖或同時禁用二者,這一設定對該表上的所有索引都生效。函式 SET_INDEX_OPTION() 則用於禁用某一索引上的頁級鎖、行級鎖或同時禁用二者。
例如,當用戶只需要修改索引中某定長欄位時,修改操作不會造成 B 樹的分裂與合併,此時就可以禁用該索引的頁級鎖。又如,當所有的使用者都只做插入操作時,使用者之間並不會對同一元組進行操作,此時就可以禁用行級鎖。當用戶能保證不對錶進行增、刪、改,而只是進行查詢時,則可以同時禁用該表上的頁級鎖和行級鎖,此時併發度最高。
6. 死鎖處理
解決死鎖問題的三種方法:預防死鎖、檢測死鎖及避免死鎖。死鎖預防要求使用者程序事先申報所需的資源或按嚴格的規程申請資源,而死鎖檢測原則上應允許死鎖發生,在適當的時機檢查,若發生死鎖,則設法排除之。與預防死鎖相比,後者過於放手,致使死鎖頻繁。而避免死鎖則以事務撤消為前提,當不能獲得資源批准時,立刻進行死鎖檢測。它既不象預防死鎖那樣過於保守,也不象死鎖檢測那樣過於放開,由於檢測及時,由歸納法可知,在已獲准等待的事務中,不可能存在死鎖,所以檢測演算法比較簡單。
DM4系統採用的是避免死鎖方法。每當一個事務所申請佔有的資源不能被立即獲得時,便進行死鎖檢測,不存在死鎖,則該事務入等待佇列。否則,DM4視為產生執行時錯誤,將當前語句回滾。採用這種機制,從使用者的角度看,DM4不存在解鎖問題。
7. 加索引和不加索引的封鎖區別
加索引和不加索引的情況下,DM的封鎖機制會影響到實際的封鎖範圍。索引的作用就在於,可以在查詢中減少對無關資料的掃描。而在一般的隔離級中,總是要對掃描到的資料進行封鎖。所以,利用索引可以減少封鎖的數量,衝突的可能性也會大大減少。