mysql-兩階段加鎖協議
前言此篇部落格主要是講述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節.
此書乃是關於資料庫事務的聖經,無需解釋(中文翻譯雖然晦澀,也能堅持讀下去,強烈推薦)
工程實踐中的兩階段加鎖-s2pl
在實際情況下,sql是千變萬化、條數不定的,資料庫很難在事務中判定什麼是加鎖階段,什麼是解鎖階段。於是引入了s2pl(strict-2pl),即:
在事務中只有提交(commit)或者回滾(rollback)時才是解鎖階段,
其餘時間為加鎖階段。
如下圖所示:
這樣的話,在實際的資料庫中就很容易實現了。
兩階段加鎖對效能的影響
上面很好的解釋了兩階段加鎖,現在我們分析下其對效能的影響。考慮下面兩種不同的扣減庫存的方案:
方案1:
begin;
// 扣減庫存
update t_inventory set count=count-5 where id=$ and count >= 5;
// 鎖住使用者賬戶表
select * from t_user_account where user_id=123 for update;
// 插入訂單記錄
insert into t_trans;
commit;
方案2:
begin;
// 鎖住使用者賬戶表
select * from t_user_account where user_id=123 for update;
// 插入訂單記錄
insert into t_trans;
// 扣減庫存
update t_inventory set count=count-5 where id=$ and count >= 5;
commit;
由於在同乙個事務之內,這幾條對資料庫的操作應該是等價的。但在兩階段加鎖下的效能確是有比較大的差距。兩者方案的時序如下圖所示:
由於庫存往往是最重要的熱點,是整個系統的瓶頸。那麼如果採用第二種方案的話,
tps應該理論上能夠提公升3rt/rt=3倍。這還僅僅是業務就只有三條sql的情況下,
多一條sql就多一次rt,就多一倍的時間。
值得注意的是:
在更新到資料庫的那個時間點才算鎖成功
提交到資料庫的時候才算解鎖成功
這兩個round_trip的前半段是不會計算在內的
如下圖所示:
當前只考慮網路時延,不考慮資料庫和應用本身的時間消耗。
依據s2pl的效能優化
從上面的例子中,可以看出,需要把最熱點的記錄,
放到事務最後,這樣可以顯著的提高吞吐量。更進一步:
越熱點記錄離事務的終點越近(無論是commit還是rollback)
筆者認為,先後順序如下圖:
避免死鎖
這也是任何sql加鎖不可避免的。上文提到了按照記錄key的熱度在事務中倒序排列。
那麼寫**的時候任何可能併發的sql都必須按照這種順序來處理,不然會造成死鎖。如下圖所示:
select for update和update where 謂詞計算
我們可以直接將一些簡單的判斷邏輯寫到update的謂詞裡面,以減少加鎖時間,考慮下面兩種方案:
方案1:
begin:
int count = select count from t_inventory for update;
if count >= 5:
update t_inventory set count=count-5 where id =123
commit
else
rollback
方案2:
begin:
int rows = update t_inventory set count=count-5 where id =123 and count >=5
if rows > 0:
commit;
elerollback;
時延如下圖所示:
可以看到,通過在update中加謂詞計算,少了1rt的時間。
由於update在執行過程中對符合謂詞條件的記錄加的是和select for update一致的排它鎖
(具體的鎖型別較為複雜,不在這裡描述),所以兩者效果一樣。
總結mysql採用兩階段加鎖協議實現隔離性和一致性,我們只有深入的去理解這種協議,才能更好的對我們的sql進行優化,增加系統的吞吐量。
MySql 兩階段加鎖協議
此篇部落格主要是講述mysql 僅限innodb 的兩階段加鎖 2pl 協議,而非兩階段提交 2pc 協議,區別如下 2pl,兩階段加鎖協議 主要用於單機事務中的一致性與隔離性。2pc,兩階段提交協議 主要用於分布式事務。mysql本身針對性能,還有乙個mvcc 多版本控制 控制,本文不考慮此種技術...
行鎖 兩階段鎖
為什麼最可能影響併發的事務盡量往後放?什麼是死鎖?什麼是兩階段鎖?死鎖檢測策略?各有什麼利弊 怎麼樣避免死鎖?在innodb事務中,行鎖是在需要的時候才加上的,但並不是不需要了就釋放了,而是等待事務結束時才釋放,這個就是兩階段鎖 在執行語句的時候加鎖,在事務commit或者rollback時候釋放鎖...
兩階段提交協議
閱讀次數 142次 類別 我的文章 永久鏈結 trackback 實現分布式事務的關鍵就是兩階段提交協議。在此協議中,乙個或多個資源管理器的活動均由乙個稱為事務協調器的單獨軟體元件來控制。實現分布式事務的關鍵就是兩階段提交協議。在此協議中,乙個或多個資源管理器的活動均由乙個稱為事務協調器的單獨軟體元...