數據庫事務、鎖與設計原理


事務

事務是指滿足ACID特性的一組操作,它們要麼完全地執行,要麼完全地不執行。

ACID特性

原子性(Atomicity)

一個事務必須被視爲一個不可分割的最小工作單元,整個事務中的所有操作要麼全部提交成功,要麼全部失敗回滾。

一致性(Consistency)

數據庫總是從一個一致性的狀態轉換到另外一個一致性狀態。事務開始和結束之間的中間狀態不會被其他事務看到。

隔離性(Isolation)

一個事務所作的修改在最終提交前,對其它事務是不可見的。

持久性(Durability)

一旦事務提交,則其所作的修改就會永久保存到數據庫中。即使系統崩潰,修改的數據也不會丟失。

理解

事務的ACID特性概念簡單,但不是很好理解,主要是因爲這幾個特性不是一種平級關係:

  • 只要滿足一致性,事務的執行結果纔是正確的。
  • 在無併發的情況下,事務串行執行,隔離性一定能夠滿足。此時只要滿足原子性,就一定能滿足一致性。
  • 在併發的情況下,多個事務並行執行,事務不僅要滿足原子性,還要滿足隔離性,才能滿足一致性。
  • 事務滿足持久化是爲了能應對數據庫崩潰的情況。

隔離級別

在併發環境下需要關注事務的隔離性,SQL標準中定義了以下四種隔離級別。

未提交讀(READ UNCOMMITTED)

事務中的修改即使沒有提交,對其他事務也都是可見的。事務可以讀取未提交的數據,這也被稱爲髒讀。

提交讀(READ COMMITTED)

一個事務從開始直到提交之前,所做的任何修改對其它事務都是不可見的。

這個級別有時候也叫做不可重複讀,因爲兩次執行同樣的查詢,可能會得到不一樣的結果。例如,T2讀取一個數據,T1對該數據做了修改並提交,如果T2再次讀取這個數據,那麼讀取的結果和第一次讀取的結果不同。

可重複讀(REPEATABLE READ)

可重複讀保證了在同一事物中多次讀取同樣記錄的結果是一致的。

該級別無法解決幻讀問題,即當某個事務在讀取某個範圍內的記錄時,另外一個事務又在該範圍內插入了新的記錄,當之前的事務再次讀取該範圍的記錄時,會產生幻行。

可串行化(SERIALIZABLE)

該級別是最高的隔離級別,通過強制事務串行執行避免上面的幻讀問題。

總結

隔離級別 髒讀 不可重複讀 幻讀 加鎖讀
未提交讀 ×
提交讀 × ×
未提交讀 × × ×
未提交讀 × × ×

當多個用戶併發地存取數據時,在數據庫中就會產生多個事務同時存取同一數據的情況。若對併發操作不加控制就可能會讀取和存儲不正確的數據,破壞數據庫的一致性。所以,鎖主要用於處理併發問題。

從數據庫系統角度分爲三種:排他鎖、共享鎖、更新鎖。
從程序員角度分爲兩種:一種是悲觀鎖,一種樂觀鎖。

悲觀鎖

總是假設最壞的情況,每次去拿數據的時候都認爲別人會修改,所以每次在拿數據的時候都會上鎖,這樣別人想拿這個數據就會阻塞直到它拿到鎖。

傳統的關係數據庫裏用到了很多這種鎖機制,比如按使用性質劃分的讀鎖、寫鎖和按作用範圍劃分的行鎖、表鎖。

共享鎖

共享鎖(S鎖)又稱爲讀鎖,若事務T對數據對象A加上S鎖,則事務T只能讀A;其他事務只能再對A加S鎖,而不能加X鎖,直到T釋放A上的S鎖。這就保證了其他事務可以讀A,但在T釋放A上的S鎖之前不能對A做任何修改。

排他鎖

排他鎖(X鎖)又稱爲寫鎖,若事務T對數據對象A加上X鎖,則只允許T讀取和修改A,其他任何事務都不能再對A加任何類型的鎖,直到T釋放A上的鎖。這就保證了其他事務在T釋放A上的鎖之前不能再讀取和修改A。

表鎖

每次操作鎖住整張表,開銷小,加鎖快,鎖粒度大,發生鎖衝突的概率最高,併發度最低。

行鎖

每次操作鎖住一行數據,開銷大,加鎖慢,鎖粒度小,發生鎖衝突的概率最低,併發度最高。

數據庫能夠確定哪些行需要鎖的情況下使用行鎖,如果不知道會影響哪些行的時候就會使用表鎖。

樂觀鎖

總是假設最好的情況,每次去拿數據的時候都認爲別人不會修改,所以不會上鎖,但是在更新的時候會判斷一下在此期間別人有沒有去更新這個數據,可以使用版本號機制和CAS算法實現。

版本號機制

一般是在數據表中加上一個數據版本號version字段,表示數據被修改的次數,當數據被修改時,version值會加一。當線程A要更新數據值時,在讀取數據的同時也會讀取version值,在提交更新時,若剛纔讀取到的version值與當前數據庫中的version值相等時才更新,否則重試更新操作,直到更新成功。

CAS算法

CAS即compare and swap(比較並交換),是一種有名的無鎖算法,在不使用鎖的情況下實現多線程之間的變量同步,也就是在沒有線程被阻塞的情況下實現變量的同步,所以也叫非阻塞同步。CAS算法涉及到三個操作數:

  • 要更新的變量V
  • 預期的值E
  • 新值N

僅當V值等於E值時,纔會將V的值設置成N,否則什麼都不做。最後CAS返回當前V的值。CAS算法需要你額外給出一個期望值,也就是你認爲現在變量應該是什麼樣子,如果變量不是你想象的那樣,就說明已經被別人修改過,就重新讀取,再次嘗試修改即可。

因爲CAS需要在操作值的時候檢查下值有沒有發生變化,如果沒有發生變化則更新,但如果一個值原來是A,變成了B,又變成了A,那麼使用CAS進行檢查時就會誤以爲它的值沒有發生變化,這個問題稱爲ABA問題。ABA問題的解決思路就是使用版本號。在變量前面追加上版本號,每次變量更新的時候把版本號加一,那麼A-B-A就會變成1A-2B-3A,以此來防止不恰當的寫入。

兩種鎖的適用場景

樂觀鎖適用於寫比較少的情況下(多讀場景),即衝突真的很少發生的時候,這樣可以省去了鎖的開銷,加大了系統的整個吞吐量。但如果是多寫的情況,一般會經常產生衝突,這就會導致上層應用會不斷的進行重試,這樣反倒是降低了性能,所以一般多寫的場景下用悲觀鎖比較合適。

關係型數據庫設計

函數依賴

部分函數依賴

設X、Y是關係R的兩個屬性集合,存在X→Y,若X’是X的真子集,存在X’→Y,則稱Y部分函數依賴於X。

完全函數依賴

設X、Y是關係R的兩個屬性集合,X’是X的真子集,存在X→Y,但對每一個X’都有X’ !→Y,則稱Y完全函數依賴於X。

傳遞函數依賴

設X、Y、Z是關係R中互不相同的屬性集合,存在X→Y(Y !→X),Y→Z,則稱Z傳遞函數依賴於X。

範式

第一範式(1NF)

在任何一個關係數據庫中,第一範式(1NF)是對關係模式的基本要求,不滿足第一範式(1NF)的數據庫就不是關係數據庫。

所謂第一範式(1NF)是指數據庫表的每一列(每個屬性)都是不可分割的基本數據項,同一列中不能有多個值,即實體中的某個屬性不能有多個值或者不能有重複的屬性。簡而言之,第一範式就是無重複的列。

第二範式(2NF)

第二範式(2NF)要求實體的屬性完全依賴於主關鍵字。

第三範式(3NF)

在滿足第二範式的基礎上,且不存在傳遞函數依賴,那麼就是第三範式。簡而言之,第三範式就是屬性不依賴於其它非主屬性。

ER圖

ER圖由三個部分組成:實體、屬性、聯繫。

參考資料

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章