事務併發的可能問題與其解決方案

2021-10-02 08:16:34 字數 3723 閱讀 1217

這些問題可以歸結為5類,包括3類資料讀問題( 髒讀、 不可重複讀和 幻象讀)以及2類資料更新問題( 第一類丟失更新和 第二類丟失更新)

a事務讀取b事務尚未提交的更改資料,並在這個資料的基礎上操作。如果恰巧b事務回滾,那麼a事務讀到的資料根本是不被承認的。來看取款事務和轉賬事務併發時引發的髒讀場景:

在這個場景中,b希望取款500元而後又撤銷了動作,而a往相同的賬戶中轉賬100元,就因為a事務讀取了b事務尚未提交的資料,因而造成賬戶白白丟失了500元。在oracle資料庫中,不會發生髒讀的情況。  

不可重複讀是指a事務讀取了b事務已經提交的更改資料。假設a在取款事務的過程中,b往該賬戶轉賬100元,a兩次讀取賬戶的餘額發生不一致: 

這個就我自己覺得問題不大,好像也並沒有影響什麼。

a事務讀取b事務提交的新增資料,這時a事務將出現幻象讀的問題。幻象讀一般發生在計算統計資料的事務中,舉乙個例子,假設銀行系統在同乙個事務中,兩次統計存款賬戶的總金額,在兩次統計過程中,剛好新增了乙個存款賬戶,並存入100元,這時,兩次統計的總金額將不一致:  

如果新增資料剛好滿足事務的查詢條件,這個新資料就進入了事務的視野,因而產生了兩個統計不一致的情況。 

不可重複讀 和 幻讀, 這兩者確實非常相似。不可重複讀 主要是說多次讀取一條記錄, 發現該記錄中某些列值被修改過。而幻讀 主要是說多次讀取乙個範圍內的記錄(包括直接查詢所有記錄結果或者做聚合統計), 發現結果不一致(標準檔案一般指記錄增多, 記錄的減少應該也算是幻讀)。

a事務撤銷時,把已經提交的b事務的更新資料覆蓋了。這種錯誤可能造成很嚴重的問題,通過下面的賬戶取款轉賬就可以看出來: 

a事務覆蓋b事務已經提交的資料,造成b事務所做操作丟失:  

為了解決多個事務併發會引發的問題,進行併發控制。資料庫系統提供了四種事務隔離級別供使用者選擇。

1.read uncommitted(讀未提交): 乙個事務不能修改其他事務正在修改的資料,但可以讀取到其他事務中尚未提交的修改,這些修改如果未被提交,將會成為髒資料,oracle有ver控制,不會有髒讀。

2.read committed(讀已提交):只允許讀取已經被提交的資料,反過來講,如果乙個事務修改了某行資料且尚未提交,而第二個事務要讀取這行資料的話,那麼是不允許的。在mysql的innodb下,雖然這種操作不被允許,但mysql不會阻塞住資料的查詢操作,而是會查詢出資料被修改之前的備份,返回給客戶端。mysql的這種機制稱為mvcc(多版本併發控制),就是說資料庫在事務併發的過程中對資料維護多個版本,使得不同的事務對不同的資料版本進行讀寫(mvcc的實現參見引用中的文章)。這樣的機制反映在應用中就是,在任何時候對資料庫查詢總是可以得到資料庫中最近提交的資料。為被提交的髒資料被隔離起來,無法被查詢到,即防止髒讀發生。

3.repeat read(可重複讀): repeat read又比read committed更加嚴格一點,但仍然是在二級封鎖協議的範疇,只是讀取過程受到更多mvcc的影響。在read committed下,允許乙個事務中多次相同查詢得到不同的結果,就是所謂的不可重複讀問題。這在一些應用中是允許的,所以oracle、sql server上預設這一隔離級別,但mysql沒有,它預設repeat read級別。在這一級別下,有賴於mvcc,同乙個事務中的查詢只能查到版本號不高於當前事務版本的資料,即事務只能看到該事務開始前或者被該事物影響的資料。反過來說,這一級別下,不允許事務讀取在該事務開始後新提交的資料。即防止了不可重複讀的發生。

依靠上面的機制,已經做到了在事務內資料內容的不變,但是不能保證多次查詢得到的資料數量一致。因為在乙個事務執行的過程中別的事務完全可以執行資料插入,當插入了剛好符合查詢條件的資料時,就會引發資料查詢結果集增加,引發幻讀。還有一種情況就是,如果乙個事務想插入一條資料,而另乙個事務已經插入了含有相同主鍵的資料,那麼當前事務也會被阻塞,並最終執行失敗,雖然當前事務根本無法查詢到這一條資料,這也是一種幻讀。

4.serializable(可序列化): 最強事務隔離機制serializable,它遵循**封鎖協議,使得所有的事務必須序列化執行,只要有事務在對錶進行查詢,那麼在此事務提交前,任何其他事務的修改都會被阻塞。這解決了一切併發問題,但會造成大量的等待、阻塞甚至死鎖,使系統效能降低,一般採用repeat read和資料庫鎖相結合方式來替代它。

事務讀不阻塞其他事務讀和寫,事務寫阻塞其他事務寫但不阻塞讀。

可以通過寫操作加「持續-x鎖」實現。

事務讀不會阻塞其他事務讀和寫,事務寫會阻塞其他事務讀和寫。

可以通過寫操作加「持續-x」鎖,讀操作加「臨時-s鎖」實現。

事務讀會阻塞其他事務事務寫但不阻塞讀,事務寫會阻塞其他事務讀和寫。

可以通過寫操作加「持續-x」鎖,讀操作加「持續-s鎖」實現。

「行級鎖」做不到,需使用「表級鎖」。

如果乙個並行排程的結果等價於某乙個序列排程的結果,那麼這個並行排程是可序列化的。

區分事務隔離級別是為了解決髒讀、不可重複讀和幻讀三個問題的。

事務隔離級別

回滾覆蓋

髒讀不可重複讀

提交覆蓋

幻讀讀未提交

x可能發生

可能發生

可能發生

可能發生

讀已提交xx

可能發生

可能發生

可能發生

可重複讀xx

xx可能發生

序列化***

xx1. **加鎖協議

一級封鎖協議:事務在修改資料前必須加x鎖,直到事務結束(提交或終止)才可釋放;如果僅僅是讀資料,不需要加鎖。。事務結束包括正常結束(commit)和非正常結束(rollback)。 一級封鎖協議保證事務t是可恢復的,並且可以防止兩個事務同時操作(增,刪,改)同一資料問題。在一級封鎖協議中,如果僅僅是讀資料不會加鎖的,所以它不能保證可重複讀和不讀「髒」資料。

二級封鎖協議:一級封鎖協議加上事務t在讀取資料r之前必須先對其加s鎖,讀完後方可釋放s鎖。 二級封鎖協議除防止了丟失修改,還可以進一步防止讀「髒」資料。但在二級封鎖協議中,由於讀完資料後即可釋放s鎖,所以它不能保證可重複讀。

**封鎖協議 :一級封鎖協議加上事務t在讀取資料r之前必須先對其加s鎖,直到事務結束才釋放。 **封鎖協議除防止了丟失修改和不讀「髒」資料外,還進一步防止了不可重複讀。

2. 兩段鎖協議(2-phase locking)

加鎖階段:事務在讀資料前加s鎖,寫資料前加x鎖,加鎖不成功則等待。

解鎖階段:一旦開始釋放鎖,就不允許再加鎖了。

若併發執行的所有事務均遵守兩段鎖協議,則對這些事務的任何併發排程策略都是可序列化的。

遵循兩段鎖協議的事務排程處理的結果是可序列化的充分條件,但是可序列化並不一定遵循兩段鎖協議。

事務隔離級別

加鎖協議

讀未提交

一級加鎖協議

讀已提交

二級加鎖協議

可重複讀

**加鎖協議

序列化兩段鎖協議

事務併發的可能問題與其解決方案

lost update 更新丟失 a.第一類更新丟失,回滾覆蓋 撤消乙個事務時,在該事務內的寫操作要回滾,把其它已提交的事務寫入的資料覆蓋了。b.第二類更新丟失,提交覆蓋 提交乙個事務時,寫操作依賴於事務內讀到的資料,讀發生在其他事務提交前,寫發生在其他事務提交後,把其他已提交的事務寫入的資料覆蓋了...

高併發解決方案

時常看到高併發的問題,但高併發其實是最不需要考慮的東西。為何,他虛無縹緲,很少有 真的需要這些東西,而且其中很多技術,其實你已經在用了。有這個意識就夠了,不需要時刻盯著這個問題。只有很少的 真的能達到高併發。簡單做乙個歸納,從低成本 高效能和高擴張性的角度來說有如下處理方案 1 html靜態化 2 ...

高併發解決方案

將靜態資源分離到靜態站,對靜態資源的請求打到靜態站,增加動態站的請求處理量 頁面靜態化是將程式生成的頁面儲存起來,使用模板技術如freemarker和velocity生成靜態頁面 nginx快取頁面資訊,再次請求時直接從快取中獲取,不需要重新生成,頁面快取記憶體中,提高訪問速度 具有相同處理功能的伺...