事物這個概念在數據庫中可謂是最爲常見。它是指一些列操做序列(一個或一個以上)當一個事務被提交給了DBMS(數據庫管理系統),則DBMS須要確保該事務中的全部操做都成功完成且其結果被永久保存在數據庫中,若是事務中有的操做沒有成功完成,則事務中的全部操做都須要被回滾,回到事務執行前的狀態(要麼全執行,要麼全都不執行);同時,該事務對數據庫或者其餘事務的執行無影響,全部的事務都好像在獨立的運行。從事物的概念出發。就能夠引出事物的四大特性。算法
一些形式化的定義爲了方便後面的描述數據庫
A,B,C來表示數據對象併發
R(A),W(B) 來表示對於數據對象的讀寫操做ui
這裏感受cmu ppt裏面對於四種特性一句話的歸納特別好。spa
首先考慮這樣一個問題。加入我把當前帳戶的$100取出轉帳給andy。可是在咱們取出它以後,轉帳給andy以前這個事物忽然終止了。或者停電了。這樣若是dbms什麼都不作,就有100 ¥ 蒸發掉。那麼如何解決這一問題。3d
1. Logging(日誌)日誌
dmbs的日誌會記錄全部的行爲。這樣就能夠當事物被abort的時候撤銷這個事物已經執行了的無效行爲。幾乎全部的DBMS
都是用了這種方法code
2. Shadow Paging對象
在這種機制下DBMS複製全部的page。當事物對這些page進行改變的時候。會改變這些page的副本。只有當這個事物成功commit以後。這些被改變的副本就會對其餘用戶可見。blog
併發協議指的是。dbms如何控制多個事物的交錯執行。
兩種最多見的協議分別是
下面來看一些併發交錯執行的例子
Assume at first A and B each have $1000.
左邊T1先執行T2後執行整個執行過程大概以下
T1:
A = A - 100 = 900
B = B + 100 = 1100
T2 :
A = A * 1.06 = 954
B = B * 1.06 = 1166
右邊則T2先執行。T1後執行。執行的結果和左邊是徹底一致的
能夠發現對於左邊。雖然在兩個事物之間有交叉。可是最後的結果是同樣的。咱們說左邊這個調度是可序列化的。
對於上面的操做。他最後的結果和序列化的結果不同。則這個調度就是不正確的
發生衝突的操做主要分爲下面三大類
讀寫衝突形成的問題就是不可重複讀的問題
事物T1先開始執行。首先讀出來A的值發現位10。接下來執行權交給了事物T2運行。T2對A進行了寫操做。可是對於事物T1而言,它會認爲它沒有對A進行了修改操做。所以當事物T2提交以後,執行權回到T1的時候,T1再讀取A發現讀出來的爲19。這裏就存在這錯誤。
讀未提交的數據(髒讀)
這裏發生的問題就是。事物T1先修改了A的值,可是後面這個事物被abort了。可是T2覺得這個值已經被修改了。因此它直接讀了T1修改完以後讀值進行了操做。
這裏對於T1而言它不知道A被從新寫了兩次。
首先經過兩個例子看一下有衝突的能夠序列化的調度。和有衝突不能序列化的調度
衝突的幾種狀況在上面已經介紹過了
能夠發現左邊和右邊並不等價。
那如何判斷一個調度是不是衝突可序列化的那。最經常使用的算法就是依賴圖算法(lab4中也有用到這個)
點:事物來表示點
邊:若是事物Ti的一個操做Oi與事物Tj的一個操做Oj發生衝突。而且操做Oi發生在Oj以前。那麼存在一條從事物Ti指向事物Tj的邊
若是出現環。則沒法衝突可序列化
除了依賴圖以外。還有一個算法來判斷是否能夠衝突序列化
這個算法讓咱們站到一個更高的層次來判斷是否能夠序列化。若是兩個調度S和S‘知足下面的條件。則咱們說這兩個調度等價
對於上面這個例子。若是是基於依賴圖算法的話。那麼它就是一個沒法衝突可序列化的調度。可是根據VIEW SERIALIZABILITY算法
。它能夠轉換成一個可序列化的算法
對於這兩個調度。咱們站到更高的層次上來看。對於讀操做。都是讀到最剛開始A的值。對於寫操做。寫的順序雖然交換了。可是並不影響結果。
最經典。也是實驗要用到的併發協議。
首先爲何叫兩段鎖協議那。確定是由於它有兩個階段吧(:。
階段1:Growing
這一階段每一個事物須要從DBMS的鎖管理處得到鎖
階段2:Shrinking
這一階段事物只容許釋放以前得到的鎖。而不容許加上新的鎖
在看一個例子以前。先來看一下這個協議中的鎖的類型。
兩大類的鎖分別爲排他鎖和共享鎖
從這個例子來看二階段鎖彷佛能夠完美的運行。事物T1先開始執行。因爲這一事物的操做中有寫操做。因此事物T1對A加上排他鎖。這樣當事物T2開始的時候。因爲事物T2也想寫A。因此事物T2也去嘗試加上排他鎖。可是因爲A此時有一個排他鎖。(事物T1持有的。此時尚未釋放)因此事物T2這個時候就須要wait
。等到事物T釋放對於A的鎖以後。事物T2才能得到對於A的加鎖權。隨後事物T2對A執行寫操做。
可是二階段鎖有他的缺點
好比對於上面這種狀況。事物T1先修改了A。可是這個事物後面ABORT
掉了。可是T2並不知道,因此T2這裏仍是會讀出A的值。而後在進行修改。這樣就可能產生連鎖錯誤。也就是上面提到的。髒讀。因此爲了解決這種問題。DBMS會由於T1的abort也把T2 abort掉。
看下面這個例子。有兩個事物。分別以下
先看沒有2PL的狀況
總感受這裏應該是1900。不過確定有問題就是了。下面看引入2PL。如何解決這一問題
這裏符合2PL。加鎖和解鎖兩個階段徹底分開。並且對於有寫操做的加X鎖。對於只須要讀操做的加S鎖
嚴格的2PL協議是說若是這個事物。若是一個value要被一個事物寫的話。那麼在這個事物完成以前。其餘的事物都不容許讀或者重寫它
這樣就能夠避免。髒讀的出現。
死鎖現象是因爲兩個事物循環等待鎖。致使的
如何探測死鎖在上面的依賴圖算法有說過
根據必定的原則選擇一個犧牲者。(能夠是年齡最小、最大、或者是最近最少使用等等原則)。而後決定回滾多少關於這個事物的改變。
有兩種不一樣的策略來實現這一機制
這是一種基於搶佔的機制
這裏的優先級就是它開始的時間戳。
[Attention]: 這裏的事物若是回滾了。它會以它以前的時間戳重啓
用下面這個例子來解釋一下上面的策略。
wait-Die
而言。優先級高的要等待優先級低的釋放。因此T1就會等待。可是若是按照wait-wait
而言t2就會被aborts掉。這種機制下。一個事物等待鎖的時間是有限制的。若是超時的話它就會回滾和重啓。
這種機制很是容易實現。可是問題就是如何界定這個超時時間
下面來看一下意向鎖的做用:參考自https://www.zhihu.com/question/51513268/answer/834445344
事務A鎖住了表中的一行,讓這一行只能讀(數據庫自動給該表增長意向共享鎖),不能寫。以後,事務B申請整個表的寫鎖。那麼事物B會怎麼作那。咱們來看沒有意向鎖的
若是沒有意向鎖則是這樣的:
step1:判斷表是否已被其餘事務用表鎖鎖表
step2:判斷表中的每一行是否已被行鎖鎖住。
若是沒有意向鎖的話,則須要遍歷全部整個表判斷是否有行鎖的存在,以避免發生衝突
那麼若是增長了意向鎖以後那
step1:判斷表是否已被其餘事務用表鎖鎖表
step2:發現表上有意向共享鎖,說明表中有些行被共享行鎖鎖住了,所以,事務B申請表的寫鎖會被阻塞。
若是有了意向鎖,只須要判斷該意向鎖與即將添加的表級鎖是否兼容便可。由於意向鎖的存在表明了,有行級鎖的存在或者即將有行級鎖的存在。於是無需遍歷整個表,便可獲取結果。
首先咱們要知道意向鎖之間是互相兼容的
意向共享鎖(IS) | 意向排他鎖(IX) | |
---|---|---|
意向共享鎖(IS) | 兼容 | 兼容 |
意向排他鎖(IX) | 兼容 | 兼容 |
可是它是和普通的排他/共享鎖互斥
意向共享鎖(IS) | 意向排他鎖(IX) | |
---|---|---|
共享鎖(S) | 兼容 | 互斥 |
排他鎖(X) | 互斥 | 互斥 |
SIX:
表示這一子樹的根結點是被共享鎖鎖住。而在低層次的結點(好比葉子結點)會加排他鎖。
這裏附上帶有意向鎖的兼容矩陣
下面看一個有三個事物的例子
假設按照順序。T1有更高的優先級
這裏因爲T1要掃描整個表同時更新一些tuple。因此這裏咱們要對這個表加SIX
對於事物T2。只讀取一個tuple。因此對整個表+ IS鎖。同時對本身讀區的tuple。加S鎖,固然若是這個tuple在以前被T1加了X鎖的話則就必需要等待了⌛️
隨後對於事物T3。要掃描整個R。就要先等待了。由於整個表還有SIX鎖。就表示有某些tuple。處於X鎖
這裏要等待對於整個表的SIX意向鎖釋放以後。就能夠掃描整個表了。
簡而言之意向鎖的做用。就是能夠實現表及鎖和行級鎖共存。
表X鎖
被佔有,則其餘事務嘗試得到 IS
、IX
均會阻塞,也就不能繼續獲取行X鎖
或行S鎖
了.表S鎖
被佔有,則其餘事務能夠得到IS
, 而得到IX
會阻塞. 從而阻止其餘事務得到行X鎖
這一部分是書上的內容。就是講解鎖管理機制是如何管理鎖表的。
是利用上圖的鏈式哈希表來實現對於鎖得管理。能夠看到對於元素I23
事物T一、T8正在持有鎖。而事物T2正在等待鎖。好比T一、T8正在讀該元素所以它們兩個都加了S鎖。可是T2想要修改這個元素也就是T2想要加X鎖。所以這裏T2就必須等待T一、T8。S鎖的釋放。這裏也是咱們lab4實現整個鎖管理的主要結構
以前咱們的注意力都在讀操做和寫操做上。下面咱們把目光彙集到插入和刪除等操做上。
咱們用\(I_i\) 表示事物Ti的操做。用\(I_j\) 表示事物Tj的操做。Let \(I_i = delete(Q)\)。咱們考慮不一樣種類的\(I_j\)
對於插入操做。咱們沒法對一個不存在的元素。進行讀和寫。在2PL協議中。插入操做就能夠當作寫操做。也就是說能夠得到X鎖。若是Ti要執行Insert(Q)
操做。那麼Ti會給新加入的元素Q一個X鎖。