數據庫中的併發控制

數據庫中的併發控制

170 total views, 3 views today

任何時候當有多個查詢想要操作相同的數據的時候便會產生併發問題,而這很有可能會導致數據庫陷入一種不一致的狀態。如果恰好出現問題的數據是一批關鍵數據,那這個後果往往可能是致命的,因此如何控制併發是數據庫中一個很重要的話題。

數據庫控制併發的方式無外乎兩種: * 悲觀併發控制 * 樂觀併發控制

其中悲觀控制是最爲常見的一種控制方式,我們所熟知的鎖就屬於悲觀併發控制。樂觀併發控制又被稱爲樂觀鎖,但其實這裏並不存在一把真正意義上的鎖,樂觀鎖更多的是一種控制機制,類似於協議一樣的東西,只要遵守便能達到併發控制的效果。

事務及 ACID

另外一個跟數據庫併發控制息息相關的概念是 事務 。你可以從 wiki 上獲取關於事務的詳細信息。數據庫在執行事務操作的時候,爲了保證事務的正確性可靠性,需要滿足四個特性,也就是我們所熟知的 ACID。它們分別是: * 原子性Atomicity) * 一致性Consistency) * 隔離性Isolation) * 持久性Durability)

在不對事務的併發性做任何限制的情況下(捨棄掉隔離性),多個併發的事務可能產生如下一些有趣的結果: * 更新丟失 這很容易理解。針對同一條數據,比如一件商品的庫存,原始存量爲 100。現在查詢 A 讀出 100,並對其做+1 操作;同一時刻,查詢 B 也讀出原始存量 100,並對其做+2 操作;我們期望的是現在最新的庫存量應該是 103,然而很不幸,如果沒有任何併發控制,商品的庫存有可能是 101 也有可能是 102。原因就在於查詢 A 的更新動作丟失了,被查詢 B 覆蓋掉了(反之亦然)。 * 髒讀 髒讀通常發生在一個查詢讀取到了另一個還未提交的事務的某個中間狀態值。由於事務還未提交,所以讀取到的這個中間狀態值有可能被進一步更改,也有可能被完全撤銷(取決於事務的業務邏輯)。 * 不可重複讀 簡單說就是一個事務中針對同一份數據如果重複讀取多次,則可能會讀取到不同值。這通常是因爲在多次讀取的間隙中,另外一個事務修改了這份數據。 * 幻讀 幻讀可以解釋爲在一個事務中多次執行 select 語句,可能會拉取到之前並不存在的數據行(其它事務有新插入),也有可能會拉取不到之前存在過的數據行(其它事務進行了刪除動作)

幻讀和不可重複讀可能會有些混淆,因爲二者都表現爲在同一個事務中重複執行讀動作,會觀察到不同的數據值。我們可以這樣理解:不可重複讀側重於數據被莫名的更新,而幻讀則更側重於莫名其妙的增加或減少了某些數據

事務隔離

很顯然,大多數情況下我們並不希望我們的數據庫會發生上述這樣一些匪夷所思的情況。之所以會說大多數情況下,是因爲在某些場合中對數據的一致性要求並不是特別高,我們可能會通過捨棄部分數據一致性來換取更高的性能(在併發環境下這通常意味着更高的吞吐量),於是髒讀、不可重複讀以及幻讀也都是有可能會允許發生的。

我們通過對事務施加隔離來實現併發控制的目的,SQL 標準定義四種隔離級別,它們分別是: 讀未提交(Read Uncommitted)、 讀已提交(Read Committed)、 可重複讀(Repeatable Read)以及 可串行化(Serializable)。

數據庫鎖也就在這個時候正式進入我們的視野,作爲實現事務隔離的一種手段添加進來。

接下來我們按隔離性由弱到強依次來看看上面提到的四種隔離級別: * 讀未提交 在這種隔離級別下,上面提到的幾種現象中除了 更新丟失 ,其它的都有可能會發生。數據庫是通過讓讀取操作不施加任何鎖來實現讀未提交。因爲沒有任何鎖,所以當其它事務中執行寫操作時,該讀取操作依然可以進行

鎖簡單可以分爲共享鎖和排他鎖

數據庫爲鎖定義了兼容性,可以簡單的理解爲共享鎖可以和共享鎖相互兼容,這表示如果一個資源上已經存在一個共享鎖,那麼另一個查詢也可以在其上繼續申請共享鎖;反之,排他鎖沒辦法和任何類型的鎖相兼容,如果一個資源上已經存在一個排他鎖,那麼隨後在該資源上任何類型的鎖申請都將失敗,查詢只能是等待該排他鎖被主動釋放 事務中申請的排他鎖會一直保持佔用直到事務結束;而共享鎖,sql server 中的實現是,當持有共享鎖的資源處理完畢時會立即釋放掉該共享鎖;mysql 中則會像排他鎖一樣,一直保持佔用直到事務結束

  • 讀已提交 只需要在讀取操作上施加一個共享鎖,就可以阻止讀操作看到另一個事務中還會完成的任何寫操作結果。根據上面提到的鎖兼容性,讀操作想要申請一個共享鎖會因爲排他鎖的不兼容性導致沒辦法申請到,所以只能等到事務結束(排他鎖會一直持留到事務結束) 這是 sql server 中的默認隔離級別
  • 可重複讀 可重複讀是通過在事務中讓共享鎖的持續時間延長至整個事務結束來實現的。因爲共享鎖現在會一直持留到事務結束了,所以其它事務想要對相同數據進行寫操作,會導致沒辦法申請到排他鎖,也就有效阻止了對數據的修改 這是 mysql 中的默認隔離級別
  • 可序列化 上面的可重複讀並不能阻止幻讀的發生。結合前面對幻讀的定義,不難發現,要想阻止幻讀的發生,我們不僅需要對已經存在的數據施加共享鎖設置共享鎖持續到整個事務結束,我們還需要對那些不存在的數據進行加鎖。舉例,在一次事務中,一條 select 語句篩選出商品單價在 100 元至 200 元之間的商品。如果第一輪 select 讀沒有任何商品滿足該條件的話,爲了滿足可序列化隔離級別,同一個事務中下一輪 select 語句讀也應返回 0 條數據行才行 在 sql server 中這是通過 鍵範圍鎖(key-range lock)來實現的。

參考鏈接

How does MVCC (Multi-Version Concurrency Control) work

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