MySQL兩階段加鎖

2021-09-25 14:17:00 字數 4283 閱讀 2639

此篇部落格主要是講述mysql(僅限innodb)的兩階段加鎖(2pl)協議,而非兩階段提交(2pc)協議,區別如下:

2pl,兩階段加鎖協議:主要用於單機事務中的一致性與隔離性。

2pc,兩階段提交協議:主要用於分布式事務。

mysql本身針對性能,還有乙個mvcc(多版本控制)控制,本文不考慮此種技術,僅僅考慮mysql本身的加鎖協議。

在對記錄更新操作或者(select for update、lock in share model)時,會對記錄加鎖(有共享鎖、排它鎖、意向鎖、gap鎖、nextkey鎖等等),本文為了簡單考慮,不考慮鎖的種類。

在乙個事務裡面,分為加鎖(lock)階段和解鎖(unlock)階段,也即所有的lock操作都在unlock操作之前,如下圖所示:

引入2pl是為了保證事務的隔離性,即多個事務在併發的情況下等同於序列的執行。 在數學上證明了如下的封鎖定理:

如果事務是良構的且是兩階段的,那麼任何乙個合法的排程都是隔離的。

具體的數學推到過程可以參照《事務處理:概念與技術》這本書的7.5.8.2節.

此書乃是關於資料庫事務的聖經,無需解釋(中文翻譯雖然晦澀,也能堅持讀下去,強烈推薦)

在實際情況下,sql是千變萬化、條數不定的,資料庫很難在事務中判定什麼是加鎖階段,什麼是解鎖階段。於是引入了s2pl(strict-2pl),即:

在事務中只有提交(commit)或者回滾(rollback)時才是解鎖階段,其餘時間為加鎖階段。

如下圖所示:

這樣的話,在實際的資料庫中就很容易實現了。

上面很好的解釋了兩階段加鎖,現在我們分析下其對效能的影響。考慮下面兩種不同的扣減庫存的方案:

方案1:begin;// 扣減庫存update t_inventory set

count=count

-5where

id=$ and

count >= 5;

// 鎖住使用者賬戶表

select * from t_user_account where user_id=123

forupdate;

// 插入訂單記錄

insert

into t_trans;

commit;

方案2:begin;// 鎖住使用者賬戶表select * from t_user_account where user_id=123

forupdate;

// 插入訂單記錄

insert

into t_trans;

// 扣減庫存

update t_inventory set

count=count

-5where

id=$ and

count >= 5;

commit;

由於在同乙個事務之內,這幾條對資料庫的操作應該是等價的。但在兩階段加鎖下的效能確是有比較大的差距。兩者方案的時序如下圖所示:

由於庫存往往是最重要的熱點,是整個系統的瓶頸。那麼如果採用第二種方案的話,

tps應該理論上能夠提公升3rt/rt=3倍。這還僅僅是業務就只有三條sql的情況下,

多一條sql就多一次rt,就多一倍的時間。

值得注意的是:

在更新到資料庫的那個時間點才算鎖成功

提交到資料庫的時候才算解鎖成功

這兩個round_trip的前半段是不會計算在內的

如下圖所示:

當前只考慮網路時延,不考慮資料庫和應用本身的時間消耗。

從上面的例子中,可以看出,需要把最熱點的記錄,放到事務最後,這樣可以顯著的提高吞吐量。更進一步:越熱點記錄離事務的終點越近(無論是commit還是rollback)筆者認為,先後順序如下圖:

這也是任何sql加鎖不可避免的。上文提到了按照記錄key的熱度在事務中倒序排列。 那麼寫**的時候任何可能併發的sql都必須按照這種順序來處理,不然會造成死鎖。如下圖所示:

我們可以直接將一些簡單的判斷邏輯寫到update的謂詞裡面,以減少加鎖時間,考慮下面兩種方案:

方案1:

begin:

int count = select count

from t_inventory for update;

ifcount >= 5:

update t_inventory set

count=count-5

where id =123

commit

else

rollback

方案2:

begin:introws = update t_inventory set

count=count

-5where

id =123

andcount >=5

if

rows > 0:

commit;

ele

rollback;

時延如下圖所示:

可以看到,通過在update中加謂詞計算,少了1rt的時間。

由於update在執行過程中對符合謂詞條件的記錄加的是和selectforupdate一致的排它鎖

(具體的鎖型別較為複雜,不在這裡描述),所以兩者效果一樣。

mysql採用兩階段加鎖協議實現隔離性和一致性,我們只有深入的去理解這種協議,才能更好的對我們的sql進行優化,增加系統的吞吐量。

啟示:1.兩階段加鎖:事務鎖分為兩個階段:lock和unlock。

2.兩階段加鎖的意義:保證事務隔離性,即併發事務執行結果和序列化執行結果一樣,稱為可序列化。

3.熱點資料(系統的瓶頸)放到臨近commit時,這樣該資料加鎖的時間就變短,增大系統吞吐量。

4.避免死鎖:按同一的加鎖順序(按訪問熱度)進行加鎖,否則可能出現死鎖。

5.select for update和update where 謂詞計算,update where可以減少鎖時間,相當於壓縮了sql,兩條變一條。

MySql 兩階段加鎖協議

此篇部落格主要是講述mysql 僅限innodb 的兩階段加鎖 2pl 協議,而非兩階段提交 2pc 協議,區別如下 2pl,兩階段加鎖協議 主要用於單機事務中的一致性與隔離性。2pc,兩階段提交協議 主要用於分布式事務。mysql本身針對性能,還有乙個mvcc 多版本控制 控制,本文不考慮此種技術...

mysql5 7兩階段提交 mysql兩階段提交

1.兩階段提交 mysql中經常說的wal技術,wal的全稱是write ahead logging,它的關鍵點就是先寫日誌,再寫磁碟。即當有一條記錄需要更新時,innodb引擎就會先把記錄寫到redo log裡,並更新記憶體,這個時候更新就完成了。因為如果每一次的更新操作都需要寫進磁碟,然後磁碟也...

mysql兩階段鎖協議 MySql 兩階段加鎖協議

mysql 兩階段加鎖協議 前言此篇部落格主要是講述mysql 僅限innodb 的兩階段加鎖 2pl 協議,而非兩階段提交 2pc 協議,區別如下 2pl,兩階段加鎖協議 主要用於單機事務中的一致性與隔離性。2pc,兩階段提交協議 主要用於分布式事務。mysql本身針對性能,還有乙個mvcc 多版...