全文主要參考數據庫系統概念一書以及mooc上戰德臣老師的數據庫課程
事務最基本的特性之一是隔離性,當數據庫中有多個事務併發執行的時候,隔離性不必定能保持。爲了保持事務的隔離性,系統必須對併發事務之間的相互做用加以控制,這是被稱爲併發控制機制來實現的。本文講述的機制都是保證調度是可串行化的。最經常使用機制有兩階段封鎖和快照隔離。html
關於串行化與一致性的關係:數據庫併發控制的基本目標是確保事務的併發執行不會致使數據庫一致性的丟失。能夠利用可串行性概念來達到這一目標,由於全部可串行化的調度都能保持數據庫的一致性。然而,並不是全部保證數據庫一致性的調度都是可串行化的。(也存在着容許非可串行化調度的併發控制機制,詳見數據庫系統概念25章)mysql
確保串行化的方法之一就是要求對數據項的訪問以互斥的方式進行。也就是說當一個事務訪問某個數據項時,其餘任何=事務都不能修改該數據項。實現該需求最經常使用的方法是隻容許事務訪問當前該事務持有該數據項的鎖的數據項。算法
給數據項加鎖的方式有多種,這一節只考慮兩種(這兩種也正是MySQL默認引擎(InnoDB)在行級鎖定時所使用的)sql
每一個事務都要根據將對數據項Q進行的操做類型申請適當的鎖。該請求發送給併發控制管理器,只有併發控制管理器授予所需鎖後,事務才能繼續其操做。
相容:假設事務Ti請求對數據項Q加A類型的鎖,而事務Tj(i不等於j)已在Q上擁有B類型的鎖。若是事務Ti仍能當即得到在數據項Q上A類型的鎖,則說A類型的鎖和B類型的鎖相容。相容矩陣以下所示: 數據庫
即共享型鎖與共享型鎖相容,而與排他型鎖不相容。在任什麼時候候,一個具體的數據項上可能同時有(被不一樣的事務持有的)多個共享鎖。如事務在訪問一數據項時而在數據項上已經存在可不相容類型的鎖,那麼只能等待該數據上全部不相容的鎖被釋放才能得到鎖從而對數據項進行訪問。
注意:一個事務只要還在訪問一個數據項,那麼它就必須擁有該數據項上的鎖。另外,讓事務對一個數據項做最後一次訪問後當即釋放該數據項上的鎖也未必是可取的,由於這可能破壞串行化。以下圖例子所示,事務T2看到了不一致的狀態(A+B的值),而串行化就是爲了事務開始和結束之間的中間狀態不會被其餘事務看到。因此咱們下面的討論都是創建在
不會在結束訪問一個數據項後當即釋放鎖的狀況下的(如兩階段鎖協議下的)。這也就會致使了死鎖發生的可能性的存在,但死鎖能夠經過回滾事務來解決,出現死鎖比出現不一致狀態好得多。
併發
加鎖可能會出現兩個事務都在等待對方解除它所佔用數據項上的鎖(也多是多個事務之間的循環等待),這種現象稱爲死鎖
。當死鎖發生時,系統必須回滾兩個事務中的一個。一旦某個事務回滾,該事務鎖住的數據項就被解鎖,其餘事務就能夠訪問這些數據項,繼續本身的執行例以下圖所示就必須回滾:性能
飢餓(餓死):當一個事務想要對一個數據項上加排他鎖,由於該數據項上已經有其餘事務所加的共享鎖了,所以必須等待。而在等待期間又有其餘事務對該數據項加上了共享鎖,以前的那個事務對一段時間後解除了共享鎖,但當前事務仍是要繼續等待,就這樣不斷地出現對該數據項加共享鎖的其餘事務,那麼該事務則會一直處於等待狀態,永遠不可能取得進展,這稱爲飢餓或者餓死。spa
避免餓死的方法:合適的併發控制器受權加鎖的條件能夠規避餓死狀況的發生。如,當事務Ti申請對數據項Q加M型鎖時,併發控制管理器受權加鎖的條件是3d
在這樣的受權加鎖條件下,一個加鎖請求就不會被氣候的加鎖申請阻塞。code
保證可串行性的一個協議是兩階段鎖協議。該協議要求每一個事務分兩個階段提出加鎖和解鎖申請:
例如事務T3和T4是兩階段的,T1和T2不是兩階段的。兩階段鎖協議能夠保證衝突可串行化,但沒法避免死鎖的出現。在兩階段對的事務中最後加鎖的位置稱爲鎖點(lock point)。
兩階段鎖協議有兩個增強版:嚴格兩階段鎖協議和強兩階段鎖協議
另外一類實現可串行化的技術是爲每一個事務分配一個時間戳,這個時間戳一般就是事務的開始的時間。對於每一個數據項,系統維護兩個時間戳,一個讀時間戳和一個寫時間戳。數據項的讀時間戳記錄讀
該數據項的的事務的最大(即最近的)時間戳,數據項的寫時間戳記錄寫入該數據項當前值
的事務的時間戳。時間戳用來確保在訪問衝突狀況下,事務按照時間戳的順序來訪問數據項。當按照時間戳的順序,一事務不能訪問時,該事務會被停止,而且分配一個新的時間戳從新開始。
這裏包括後面的系統一詞指的都是數據庫系統
時的時鐘值。
時間戳協議和兩段鎖協議類似,都是保證了衝突可串行化,但二者都是保證的衝突可串行化的兩個不一樣的真子集,存在知足兩階段鎖協議卻不能知足時間戳協議的調度,反之亦然。時間戳協議保證衝突可串行化的緣由在於:衝突操做是按時間戳順序進行處理的。
死鎖:時間戳協議保證了無死鎖,由於不存在等待的事務。
餓死:當一系列的短事務引發長事務反覆重啓時,可能致使長事務餓死的現象。解決方式:若是發現一個事務反覆重啓,與之衝突的事務應當暫時阻塞,以使該事務可以完成。
經過維護數據項的多個版本,一個事務容許讀取一箇舊版本的數據項,而不是被另外一個未提交或者在串行化序列中應該排在後面的事務寫入的新版本的數據項。有許多多版本併發控制技術,其中一個是實際中普遍應用的稱爲快照隔離的技術。
在快照隔離中,咱們能夠視爲每一個事務開始時都有其自身的數據庫版本或者快照(實際實現中不會複製整個數據庫,只有被改變的數據項纔會保留多個版本)。事務從這個私有版本中讀取數據,所以和其餘事務所作的更新隔離開,若是事務更新數據庫,更新只出如今其私有版本中,而不是實際的數據庫自己中。當事務提交時,和更新有關的信息將保存,使得更新被寫入真正的數據庫。
當一個事務T進入提交狀態後,只有在 沒有其餘併發事務已經修改該事務想要更新的數據項 的狀況下,事務進入提交狀態。而不能提交的事務則終止。
快照隔離能夠保證讀數據的嘗試永遠無需等待(不像鎖協議那樣要讀的數據項上面被加了排他鎖就只能等待)。只讀事務不會終止。只有修改數據的事務纔有微小的終止風險。因爲每一個事務讀取它本身的數據庫版本或快照,所以讀數據不會致使此後其餘事務的更新嘗試被迫等待(不像鎖協議要寫的數據項中被加了共享鎖後就只能等待甚至有餓死的狀況)。由於大部分事務是隻讀的,而且大多數其餘事務讀數據的狀況多於更新,因此這是與鎖相比每每能帶來性能改善的主要緣由。
矛盾在於,快照隔離帶來的問題是它提供了太多的隔離。考慮兩個事務T和T',在一個串行化調度中,要麼T看到T'所作的全部更新,要麼T'看到T所作的全部更新,由於在串行化順序中一個必須在另外一個以後。在快照隔離下,任何事務都不能看到對方的更新,這是在串行化調度中不會出現的。在許多(事實上,大多數)狀況下,兩個事務的數據訪問不會衝突,所以沒有什麼問題。DNA一旦T讀取的是T'要更新的數據項,T'讀取的是T'要更新的數據項,則可能兩個事務都沒法讀取到對方的更新。結果可能致使數據庫的不一致狀態。
詳情參看數據庫系統概念第六版中的15.5基於有效性檢查的協議、15.6多版本機制、15.7快照隔離。
注意:根據論文,快照隔離能排除掉嚴格版本的幻讀A3,但對於寬泛版本的P3(實際上使得隔離性更嚴格了)是不能排出的,此外還存在寫偏斜(write skew)的異常。
若是存在一個事務集,該集合中的每一個事務都在等待該集合中的另外一個事務,那麼咱們說系統處於死鎖狀態。更確切地說,存在一個等待事務集{T0,T1,...,Tn},使得T0正等待被T1鎖住的數據項,T1正等待被T2鎖住的數據項,....,Tn-1正等待被Tn鎖住的數據項,且Tn正等待被T0鎖住的數據項。在這種狀況下,沒有一個事務可以取得進展。
此時系統的惟一補救措施就是採起激烈的動做,如回滾某些陷於死鎖的事務。事務有可能只部分回滾,即事務回滾到 它獲得鎖的點 以前,這樣就釋放了鎖從而解決死鎖。
處理死鎖有兩種主要的方法:咱們可使用死鎖預防協議來保證系統永不進入死鎖狀態。另外一種方法是,咱們容許系統進入死鎖狀態,而後試着用死鎖檢測與死鎖恢復機制進行恢復。兩種方法均有可能引發事務回滾。若是事務進入死鎖狀態機率相對比較高,則一般使用死鎖預防機制,不然使用檢測與恢復機制。
注意:檢測與恢復機制所帶來的各類開銷,不只包括在運行時維護必要信息及執行檢測算法的代價,還要包括從死鎖中恢復所固有的潛在損失。
預防死鎖主要有兩種思路:
或
要求同時得到全部的鎖從而來保證不會發生循環等待(一次封鎖法)(就不會發生佔着一個數據項的鎖還在等着另外一個數據項的鎖的狀況,要麼就一塊兒佔着要麼就都不佔)
。 一次封鎖法就是要求每一個事務在開始以前封鎖它的全部的數據項,此外,要麼一次所有封鎖,要麼全不封鎖。
一次封鎖協議主要的缺點是:(1)在事務開始前一般很難預知哪些數據項須要封鎖。(2)數據項的使用率可能很低,由於不少數據項可能封鎖很長時間卻用不到。
順序封鎖法是對全部的數據項強加一個次序,同時要求按次序規定的順序對數據項進行加鎖。
順序封鎖法存在的問題:維護成本高。數據庫系統中可封鎖的數據對象極其衆多,而且隨數據的插入、刪除等操做而不斷地變化,要維護這樣極多並且變化的資源的封鎖順序很是困難,成本很高。
2. 第二種思路比較接近死鎖恢復,每當等待有可能致使死鎖時,進行事務回滾而不是等待加鎖。
這種思路的實現方式是使用搶佔和事務回滾。在搶佔機制中,若事務Tj所申請的鎖已經被事務Ti所持有,則授予Ti的鎖可能經過回滾事務Ti被搶佔,並將鎖授予Tj。爲控制搶佔,咱們給每一個事務賦予一個惟一的時間戳,系統僅用時間戳來決定事務應當等待仍是回滾。併發協議仍使用封鎖協議,若一個事務回滾,則該事務重啓時保持原有的時間戳。
根據此思路提出的兩種相反的預防機制:
兩種機制的缺點都是:可能會發生沒必要要的回滾。
3. 還有一種額外的思路是鎖超時機制,申請鎖的事務至多等待一段給定的時間。若此時間內爲授予該事務鎖,則稱該事務超時,則該事務會進行回滾並重啓。該機制介於死鎖預防(不會發生死鎖)和死鎖檢與恢復之間。
鎖超時機制實現起來及其容易,但其缺點在於很難肯定一個事務超時以前應等待多長時間,過長則會在死鎖時形成延遲,太短則會形成沒必要要的回滾。因此僅應用於事務主要是短事務而且長時間的等待很可能是死鎖形成的狀況下。
挖坑待填