MySQL中的悲觀鎖

什么是悲觀鎖

關(guān)系數(shù)據(jù)庫管理系統(tǒng)里,悲觀并發(fā)控制(又名“悲觀鎖”,Pessimistic Concurrency Control,縮寫“PCC”)是一種并發(fā)控制的方法。它可以阻止一個事務(wù)以影響其他用戶的方式來修改數(shù)據(jù)。如果一個事務(wù)執(zhí)行的操作讀某行數(shù)據(jù)應(yīng)用了鎖,那只有當(dāng)這個事務(wù)把鎖釋放,其他事務(wù)才能夠執(zhí)行與該鎖沖突的操作。

悲觀并發(fā)控制主要用于數(shù)據(jù)爭用激烈的環(huán)境,以及發(fā)生并發(fā)沖突時使用鎖保護數(shù)據(jù)的成本要低于回滾事務(wù)的成本的環(huán)境中。

簡而言之,悲觀鎖主要用于保護數(shù)據(jù)的完整性。當(dāng)多個事務(wù)并發(fā)執(zhí)行時,某個事務(wù)對數(shù)據(jù)應(yīng)用了鎖,則其他事務(wù)只能等該事務(wù)執(zhí)行完了,才能進行對該數(shù)據(jù)進行修改操作。

使用場景

在商品購買場景中,當(dāng)有多個用戶對某個庫存有限的商品同時進行下單操作。若采用先查詢庫存,后減庫存的方式進行庫存數(shù)量的變更,將會導(dǎo)致超賣的產(chǎn)生。

商品超賣流程圖

若使用悲觀鎖,當(dāng)B用戶獲取到某個商品的庫存數(shù)據(jù)時,用戶A則會阻塞,直到B用戶完成減庫存的整個事務(wù)時,A用戶才可以獲取到商品的庫存數(shù)據(jù)。則可以避免商品被超賣。

如何使用悲觀鎖

用法:SELECT … FOR UPDATE;

例如,

select * from tbl_user where id=1 for update;

獲取鎖的前提:結(jié)果集中的數(shù)據(jù)沒有使用排他鎖或共享鎖時,才能獲取鎖,否則將會阻塞。

需要注意的是, FOR UPDATE 生效需要同時滿足兩個條件時才生效:

  • 數(shù)據(jù)庫的引擎為 innoDB
  • 操作位于事務(wù)塊中(BEGIN/COMMIT)

體驗悲觀鎖

Step 1 初始化表結(jié)構(gòu)和數(shù)據(jù)

CREATE TABLE `tbl_user` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `status` int(11) DEFAULT NULL,
  `name` varchar(255) COLLATE utf8_bin DEFAULT NULL,
  PRIMARY KEY (`id`)
);


INSERT INTO `tbl_user` (`id`, `status`, `name`)
VALUES
    (1,1,X'7469616E'),
    (2,1,X'63697479');

Step 2

窗口1

// 關(guān)閉mysql數(shù)據(jù)庫的自動提交屬性
set autocommit=0;

// 開啟事務(wù)
BEGIN;

SELECT * FROM tbl_user where id=1 for update;

窗口2

此時,我們在窗口2執(zhí)行下面這條命令,嘗試獲取悲觀鎖:

SELECT * FROM tbl_user where id=1 for update;

執(zhí)行完后,窗口2并沒有像窗口1一樣,立刻返回結(jié)果,而是發(fā)生了阻塞。

若超時間未獲取鎖,將會得到一個鎖超時錯誤提示。如下圖所示:


獲取鎖超時

行鎖與表鎖

當(dāng)執(zhí)行 select ... for update時,將會把數(shù)據(jù)鎖住,因此,我們需要注意一下鎖的級別。MySQL InnoDB 默認為行級鎖。當(dāng)查詢語句指定了主鍵時,MySQL會執(zhí)行「行級鎖」,否則MySQL會執(zhí)行「表鎖」。

常見情況如下:

  • 若明確指明主鍵,且結(jié)果集有數(shù)據(jù),行鎖;
  • 若明確指明主鍵,結(jié)果集無數(shù)據(jù),則無鎖;
  • 若無主鍵,且非主鍵字段無索引,則表鎖;
  • 若使用主鍵但主鍵不明確,則使用表鎖;
select * from tbl_user where id<>1 for update; 

若需要了解更多情況,可以閱讀 此篇文章了解更多。

小結(jié): innoDB的行鎖是通過給索引上的索引項加鎖實現(xiàn)的,因此,只有通過索引檢索數(shù)據(jù),才會采用行鎖,否則使用的是表鎖。

總結(jié)

悲觀鎖采用的是「先獲取鎖再訪問」的策略,來保障數(shù)據(jù)的安全。但是加鎖策略,依賴數(shù)據(jù)庫實現(xiàn),會增加數(shù)據(jù)庫的負擔(dān),且會增加死鎖的發(fā)生幾率。此外,對于不會發(fā)生變化的只讀數(shù)據(jù),加鎖只會增加額外不必要的負擔(dān)。在實際的實踐中,對于并發(fā)很高的場景并不會使用悲觀鎖,因為當(dāng)一個事務(wù)鎖住了數(shù)據(jù),那么其他事務(wù)都會發(fā)生阻塞,會導(dǎo)致大量的事務(wù)發(fā)生積壓拖垮整個系統(tǒng)。

參考資料

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容