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

2021-09-07 09:24:23 字數 3713 閱讀 5387

lost update 更新丟失

a. 第一類更新丟失,回滾覆蓋:撤消乙個事務時,在該事務內的寫操作要回滾,把其它已提交的事務寫入的資料覆蓋了。

b. 第二類更新丟失,提交覆蓋:提交乙個事務時,寫操作依賴於事務內讀到的資料,讀發生在其他事務提交前,寫發生在其他事務提交後,把其他已提交的事務寫入的資料覆蓋了。這是不可重複讀的特例。

dirty read 髒讀:乙個事務讀到了另乙個未提交的事務寫的資料。

non-repeatable read 不可重複讀:乙個事務中兩次讀同一行資料,可是這兩次讀到的資料不一樣。

phantom read 幻讀:乙個事務中兩次查詢,但第二次查詢比第一次查詢多了或少了幾行或幾列資料。

兩類更新丟失的舉例

時間取款事務a

轉賬事務b

t1開始事務

t2開始事務

t3讀餘額為1000

t4取出100,餘額改為900-t5

讀餘額為1000

t6匯入100,餘額改為1100

t7提交事務,餘額定為1100

t8撤銷事務,餘額改回1000-t9

最終餘額1000,更新丟失

- 寫操作沒加「持續-x鎖」,沒能阻止事務b寫,發生了回滾覆蓋。

時間轉賬事務a

取款事務b

t1開始事務

t2開始事務

t3讀餘額為1000

t4讀餘額為1000

t5取出100,餘額改為900

t6提交事務,餘額定為900

t7匯入100,餘額改為1100-t8

提交事務,餘額定為1100-t9

最終餘額1100,更新丟失

- 寫操作加了「持續-x鎖」,讀操作加了「臨時-s鎖」,沒能阻止事務b寫,發生了提交覆蓋。

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

讀未提交

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

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

讀已提交

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

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

可重複讀

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

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

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

可序列化

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

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

事務隔離級別

回滾覆蓋

髒讀不可重複讀

提交覆蓋

幻讀讀未提交

x可能發生

可能發生

可能發生

可能發生

讀已提交xx

可能發生

可能發生

可能發生

可重複讀xx

xx可能發生

序列化***

xx這裡羅列的技術有些是資料庫系統已經實現,有些需要開發者自主完成。

1. 版本檢查

在資料庫中保留「版本」字段,跟隨資料同時讀寫,以此判斷資料版本。版本可能是時間戳或狀態字段。

下例中的 where 子句就實現了簡單的版本檢查:

update table set status = 1 where id=1 and status = 0;

版本檢查能夠作為「樂觀鎖」,解決更新丟失的問題。

2. 鎖

2.1 共享鎖與排它鎖

共享鎖(shared locks, s-locks)

基本鎖型別之一。加共享鎖的物件只允許被當前事務和其他事務讀。也稱讀鎖。

能給未加鎖和新增了s鎖的物件新增s鎖。物件可以接受新增多把s鎖。

排它鎖(exclusive locks, x-locks)

基本鎖型別之一。加排它鎖的物件只允許被當前事務讀和寫。也稱獨佔鎖,寫鎖。

只能給未加鎖的物件新增x鎖。物件只能接受一把x鎖。加x鎖的物件不能再加任何鎖。

更新鎖(update locks, u-locks)

鎖型別之一。引入它是因為多數資料庫在實現加x鎖時是執行了如下流程:先加s鎖,新增成功後嘗試更換為x鎖。這時如果有兩個事務同時加了s鎖,嘗試換x鎖,就會發生死鎖。因此增加u鎖,u鎖代表有更新意向,只允許有乙個事務拿到u鎖,該事務在發生寫後u鎖變x鎖,未寫時看做s鎖。

目前好像只在 mssql 裡看到了u鎖。

2.2 臨時鎖與持續鎖

鎖的時效性。指明了加鎖生效期是到當前語句結束還是當前事務結束。

2.3 表級鎖與行級鎖

鎖的粒度。指明了加鎖的物件是當前表還是當前行。

在這裡學習 mssql 的「鎖粒度和層次結構」

2.4 悲觀鎖與樂觀鎖

這兩種鎖的說法,主要是對「是否真正在資料庫層面加鎖」進行討論。

悲觀鎖(pessimistic locking)

悲觀鎖假定當前事務操縱資料資源時,肯定還會有其他事務同時訪問該資料資源,為了避免當前事務的操作受到干擾,先鎖定資源。悲觀鎖需使用資料庫的鎖機制實現,如使用行級排他鎖或表級排它鎖。

儘管悲觀鎖能夠防止丟失更新和不可重複讀這類問題,但是它非常影響併發效能,因此應該謹慎使用。

樂觀鎖(optimistic locking)

樂觀鎖假定當前事務操縱資料資源時,不會有其他事務同時訪問該資料資源,因此不在資料庫層次上的鎖定。樂觀鎖使用由程式邏輯控制的技術來避免可能出現的併發問題。

唯一能夠同時保持高併發和高可伸縮性的方法就是使用帶版本檢查的樂觀鎖。

樂觀鎖不能解決髒讀的問題,因此仍需要資料庫至少啟用「讀已提交」的事務隔離級別。

3. **加鎖協議

稱之為協議,是指在使用它的時候,所有的事務都必須遵循該規則!!!

一級加鎖協議

事務在修改資料前必須加x鎖,直到事務結束(提交或終止)才可釋放;如果僅僅是讀資料,不需要加鎖。

如下例:

select *** for update;

update ***;

二級加鎖協議

滿足一級加鎖協議,且事務在讀取資料之前必須先加s鎖,讀完後即可釋放s鎖。

**加鎖協議

滿足一級加鎖協議,且事務在讀取資料之前必須先加s鎖,直到事務結束才釋放。

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

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

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

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

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

兩段鎖協議和防止死鎖的一次封鎖法的異同之處

一次封鎖法要求每個事務必須一次將所有要使用的資料全部加鎖,否則就不能繼續執行,因此一次封鎖法遵守兩段鎖協議;但是兩段鎖協議並不要求事務必須一次將所有要使用的資料全部加鎖,因此遵守兩段鎖協議的事務可能發生死鎖。

事務隔離級別

加鎖協議

讀未提交

一級加鎖協議

讀已提交

二級加鎖協議

可重複讀

**加鎖協議

序列化兩段鎖協議

封鎖協議和隔離級別並不是嚴格對應的。

理解「事務隔離級別-加鎖的選擇-**加鎖協議」之間的聯絡,著實花了不少功夫。

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

這些問題可以歸結為5類,包括3類資料讀問題 髒讀 不可重複讀和 幻象讀 以及2類資料更新問題 第一類丟失更新和 第二類丟失更新 a事務讀取b事務尚未提交的更改資料,並在這個資料的基礎上操作。如果恰巧b事務回滾,那麼a事務讀到的資料根本是不被承認的。來看取款事務和轉賬事務併發時引發的髒讀場景 在這個場...

高併發解決方案

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

高併發解決方案

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