悲觀鎖總結(jié)和實(shí)踐

最近學(xué)習(xí)了一下數(shù)據(jù)庫的悲觀鎖和樂觀鎖头朱,根據(jù)自己的理解和網(wǎng)上參考資料總結(jié)如下:

悲觀鎖介紹(百科):

悲觀鎖,正如其名,它指的是對數(shù)據(jù)被外界(包括本系統(tǒng)當(dāng)前的其他事務(wù)绸栅,以及來自外部系統(tǒng)的事務(wù)處理)修改持保守態(tài)度,因此页屠,在整個(gè)數(shù)據(jù)處理過程中粹胯, 將數(shù)據(jù)處于鎖定狀態(tài)蓖柔。悲觀鎖的實(shí)現(xiàn),往往依靠數(shù)據(jù)庫提供的鎖機(jī)制(也只有數(shù)據(jù)庫層提供的鎖機(jī)制才能真正保證數(shù)據(jù)訪問的排他性风纠,否則况鸣,即使在本系統(tǒng)中實(shí)現(xiàn)了 加鎖機(jī)制,也無法保證外部系統(tǒng)不會修改數(shù)據(jù))竹观。

使用場景舉例:以MySQL InnoDB為例

商品goods表中有一個(gè)字段status镐捧,status為1代表商品未被下單,status為2代表商品已經(jīng)被下單臭增,那么我們對某個(gè)商品下單時(shí)必須確保該商品status為1懂酱。假設(shè)商品的id為1。

1如果不采用鎖誊抛,那么操作方法如下:

//1.查詢出商品信息

select status from t_goods where id=1;

//2.根據(jù)商品信息生成訂單

insert into t_orders (id,goods_id) values (null,1);

//3.修改商品status為2

update t_goods set status=2;


上面這種場景在高并發(fā)訪問的情況下很可能會出現(xiàn)問題列牺。

前面已經(jīng)提到,只有當(dāng)goods ?status為1時(shí)才能對該商品下單拗窃,上面第一步操作中瞎领,查詢出來的商品status為1。但是當(dāng)我們執(zhí)行第三步Update操作的時(shí)候并炮,有可能出現(xiàn)其他 人先一步對商品下單把goods ?status修改為2了默刚,但是我們并不知道數(shù)據(jù)已經(jīng)被修改了,這樣就可能造成同一個(gè)商品被下單2次逃魄,使得數(shù)據(jù)不一致荤西。所以說這種方式是不安全的。


2使用悲觀鎖來實(shí)現(xiàn):

在上面的場景中伍俘,商品信息從查詢出來到修改邪锌,中間有一個(gè)處理訂單的過程,使用悲觀鎖的原理就是癌瘾,當(dāng)我們在查詢出goods信息后就把當(dāng)前的數(shù)據(jù)鎖定觅丰,直到我們修改完畢后再解鎖。那么在這個(gè)過程中妨退,因?yàn)間oods被鎖定了妇萄,就不會出現(xiàn)有第三者來對其進(jìn)行修改了。


注:要使用悲觀鎖咬荷,我們必須關(guān)閉mysql數(shù)據(jù)庫的自動提交屬性冠句,因?yàn)镸ySQL默認(rèn)使用autocommit模式,也就是說幸乒,當(dāng)你執(zhí)行一個(gè)更新操作后懦底,MySQL會立刻將結(jié)果進(jìn)行提交。


我們可以使用命令設(shè)置MySQL為非autocommit模式:

set autocommit=0;


設(shè)置完autocommit后罕扎,我們就可以執(zhí)行我們的正常業(yè)務(wù)了聚唐。具體如下:

//0.開始事務(wù)

begin;/begin work;/start transaction; (三者選一就可以)

//1.查詢出商品信息

select status from t_goods where id=1?for update;

//2.根據(jù)商品信息生成訂單

insert into t_orders (id,goods_id) values (null,1);

//3.修改商品status為2

update t_goods set status=2;

//4.提交事務(wù)

commit;/commit work;


注:上面的begin/commit為事務(wù)的開始和結(jié)束丐重,因?yàn)樵谇耙徊轿覀冴P(guān)閉了mysql的autocommit,所以需要手動控制事務(wù)的提交杆查,在這里就不細(xì)表了扮惦。


上面的第一步我們執(zhí)行了一次查詢操作:select status from t_goods where id=1 for update;

與普通查詢不一樣的是,我們使用了select…for update的方式亲桦,這樣就通過數(shù)據(jù)庫實(shí)現(xiàn)了悲觀鎖径缅。此時(shí)在t_goods表中,id為1的 那條數(shù)據(jù)就被我們鎖定了烙肺,其它的事務(wù)必須等本次事務(wù)提交之后才能執(zhí)行。這樣我們可以保證當(dāng)前的數(shù)據(jù)不會被其它事務(wù)修改氧卧。


注:需要注意的是桃笙,在事務(wù)中,只有SELECT ... FOR UPDATE 或LOCK IN SHARE MODE 同一筆數(shù)據(jù)時(shí)會等待其它事務(wù)結(jié)束后才執(zhí)行沙绝,一般SELECT ... 則不受此影響搏明。拿 上面的實(shí)例來說,當(dāng)我執(zhí)行select status from t_goods where id=1 for ?update;后闪檬。我在另外的事務(wù)中如果再次執(zhí)行select status from t_goods where id=1 for ?update;則第二個(gè)事務(wù)會一直等待第一個(gè)事務(wù)的提交星著,此時(shí)第二個(gè)查詢處于阻塞的狀態(tài),但是如果我是在第二個(gè)事務(wù)中執(zhí)行select status ?from t_goods where id=1;則能正常查詢出數(shù)據(jù)粗悯,不會受第一個(gè)事務(wù)的影響虚循。


補(bǔ)充:MySQL select…for update的Row Lock與Table Lock

上面我們提到,使用select…for update會把數(shù)據(jù)給鎖住样傍,不過我們需要注意一些鎖的級別横缔,MySQL ?InnoDB默認(rèn)Row-Level Lock,所以只有「明確」地指定主鍵衫哥,MySQL 才會執(zhí)行Row lock (只鎖住被選取的數(shù)據(jù)) ?茎刚,否則MySQL 將會執(zhí)行Table Lock (將整個(gè)數(shù)據(jù)表單給鎖住)。


舉例說明:

數(shù)據(jù)庫表t_goods撤逢,包括id,status,name三個(gè)字段膛锭,id為主鍵,數(shù)據(jù)庫中記錄如下;

Sql代碼

mysql>?select?*?from?t_goods;??

+----+--------+------+??

|?id?|?status?|?name?|??

+----+--------+------+??

|??1?|??????1?|?道具?|??

|??2?|??????1?|?裝備?|??

+----+--------+------+??

2?rows?in?set??


mysql>??

注:為了測試數(shù)據(jù)庫鎖蚊荣,我使用兩個(gè)console來模擬不同的事務(wù)操作初狰,分別用console1、console2來表示妇押。?


例1: (明確指定主鍵跷究,并且有此數(shù)據(jù),row lock)

console1:查詢出結(jié)果敲霍,但是把該條數(shù)據(jù)鎖定了

Sql代碼

mysql>?select?*?from?t_goods?where?id=1?for?update;??

+----+--------+------+??

|?id?|?status?|?name?|??

+----+--------+------+??

|??1?|??????1?|?道具?|??

+----+--------+------+??

1?row?in?set??


mysql>??

console2:查詢被阻塞

Sql代碼

mysql>?select?*?from?t_goods?where?id=1?for?update;??

console2:如果console1長時(shí)間未提交俊马,則會報(bào)錯(cuò)

Sql代碼

mysql>?select?*?from?t_goods?where?id=1?for?update;??

ERROR?1205?:?Lock?wait?timeout?exceeded;?try?restarting?transaction??


例2: (明確指定主鍵丁存,若查無此數(shù)據(jù),無lock)

console1:查詢結(jié)果為空

Sql代碼

mysql>?select?*?from?t_goods?where?id=3?for?update;??

Empty?set??

console2:查詢結(jié)果為空柴我,查詢無阻塞解寝,說明console1沒有對數(shù)據(jù)執(zhí)行鎖定

Sql代碼

mysql>?select?*?from?t_goods?where?id=3?for?update;??

Empty?set??


例3: (無主鍵,table lock)

console1:查詢name=道具 的數(shù)據(jù)艘儒,查詢正常

Sql代碼

mysql>?select?*?from?t_goods?where?name='道具'?for?update;??

+----+--------+------+??

|?id?|?status?|?name?|??

+----+--------+------+??

|??1?|??????1?|?道具?|??

+----+--------+------+??

1?row?in?set??

mysql>??

console2:查詢name=裝備 的數(shù)據(jù)聋伦,查詢阻塞,說明console1把表給鎖住了

Sql代碼

mysql>?select?*?from?t_goods?where?name='裝備'?for?update;??

console2:若console1長時(shí)間未提交界睁,則查詢返回為空

Sql代碼

mysql>?select?*?from?t_goods?where?name='裝備'?for?update;??

Query?OK,?-1?rows?affected??


例4: (主鍵不明確觉增,table lock)

console1:查詢正常

Sql代碼

mysql>?begin;??

Query?OK,?0?rows?affected??


mysql>?select?*?from?t_goods?where?id>0?for?update;??

+----+--------+------+??

|?id?|?status?|?name?|??

+----+--------+------+??

|??1?|??????1?|?道具?|??

|??2?|??????1?|?裝備?|??

+----+--------+------+??

2?rows?in?set??


mysql>??

console2:查詢被阻塞,說明console1把表給鎖住了

Sql代碼

mysql>?select?*?from?t_goods?where?id>1?for?update;??


例5: (主鍵不明確翻斟,table lock)

console1:

Sql代碼

mysql>?begin;??

Query?OK,?0?rows?affected??


mysql>?select?*?from?t_goods?where?id<>1?for?update;??

+----+--------+------+??

|?id?|?status?|?name?|??

+----+--------+------+??

|??2?|??????1?|?裝備?|??

+----+--------+------+??

1?row?in?set??


mysql>??

console2:查詢被阻塞逾礁,說明console1把表給鎖住了

Sql代碼

mysql>?select?*?from?t_goods?where?id<>2?for?update;??

console1:提交事務(wù)

Sql代碼

mysql>?commit;??

Query?OK,?0?rows?affected??

console2:console1事務(wù)提交后,console2查詢結(jié)果正常

Sql代碼

mysql>?select?*?from?t_goods?where?id<>2?for?update;??

+----+--------+------+??

|?id?|?status?|?name?|??

+----+--------+------+??

|??1?|??????1?|?道具?|??

+----+--------+------+??

1?row?in?set??


mysql>??


以上就是關(guān)于數(shù)據(jù)庫主鍵對MySQL鎖級別的影響實(shí)例访惜,需要注意的是嘹履,除了主鍵外,使用索引也會影響數(shù)據(jù)庫的鎖定級別


舉例:

我們修改t_goods表债热,給status字段創(chuàng)建一個(gè)索引

修改id為2的數(shù)據(jù)的status為2砾嫉,此時(shí)表中數(shù)據(jù)為:

Sql代碼

mysql>?select?*?from?t_goods;??

+----+--------+------+??

|?id?|?status?|?name?|??

+----+--------+------+??

|??1?|??????1?|?道具?|??

|??2?|??????2?|?裝備?|??

+----+--------+------+??

2?rows?in?set??


mysql>??


例6: (明確指定索引,并且有此數(shù)據(jù)窒篱,row lock)

console1:

Sql代碼

mysql>?select?*?from?t_goods?where?status=1?for?update;??

+----+--------+------+??

|?id?|?status?|?name?|??

+----+--------+------+??

|??1?|??????1?|?道具?|??

+----+--------+------+??

1?row?in?set??


mysql>??

console2:查詢status=1的數(shù)據(jù)時(shí)阻塞焕刮,超時(shí)后返回為空,說明數(shù)據(jù)被console1鎖定了

Sql代碼

mysql>?select?*?from?t_goods?where?status=1?for?update;??

Query?OK,?-1?rows?affected??

console2:查詢status=2的數(shù)據(jù)舌剂,能正常查詢济锄,說明console1只鎖住了行,未鎖表

Sql代碼

mysql>?select?*?from?t_goods?where?status=2?for?update;??

+----+--------+------+??

|?id?|?status?|?name?|??

+----+--------+------+??

|??2?|??????2?|?裝備?|??

+----+--------+------+??

1?row?in?set??


mysql>??


例7: (明確指定索引霍转,若查無此數(shù)據(jù)荐绝,無lock)

console1:查詢status=3的數(shù)據(jù),返回空數(shù)據(jù)

Sql代碼

mysql>?select?*?from?t_goods?where?status=3?for?update;??

Empty?set??

console2:查詢status=3的數(shù)據(jù)避消,返回空數(shù)據(jù)

Sql代碼

mysql>?select?*?from?t_goods?where?status=3?for?update;??

Empty?set??

感興趣的可以自己來我的Java架構(gòu)群低滩,可以獲取免費(fèi)的學(xué)習(xí)資料,群號:855801563?對Java技術(shù)岩喷,架構(gòu)技術(shù)感興趣的同學(xué)恕沫,歡迎加群,一起學(xué)習(xí)纱意,相互討論婶溯。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子迄委,更是在濱河造成了極大的恐慌褐筛,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件叙身,死亡現(xiàn)場離奇詭異渔扎,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)信轿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進(jìn)店門晃痴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人财忽,你說我怎么就攤上這事倘核。” “怎么了即彪?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵笤虫,是天一觀的道長。 經(jīng)常有香客問我祖凫,道長,這世上最難降的妖魔是什么酬凳? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任惠况,我火速辦了婚禮,結(jié)果婚禮上宁仔,老公的妹妹穿的比我還像新娘稠屠。我一直安慰自己,他們只是感情好翎苫,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布权埠。 她就那樣靜靜地躺著,像睡著了一般煎谍。 火紅的嫁衣襯著肌膚如雪攘蔽。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天呐粘,我揣著相機(jī)與錄音满俗,去河邊找鬼。 笑死作岖,一個(gè)胖子當(dāng)著我的面吹牛唆垃,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播痘儡,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼辕万,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起渐尿,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤醉途,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后涡戳,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體结蟋,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年渔彰,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了嵌屎。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,675評論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡恍涂,死狀恐怖宝惰,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情再沧,我是刑警寧澤尼夺,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站炒瘸,受9級特大地震影響淤堵,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜顷扩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一拐邪、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧隘截,春花似錦扎阶、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至犀农,卻和暖如春惰赋,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背呵哨。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工谤逼, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人仇穗。 一個(gè)月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓流部,卻偏偏與公主長得像,于是被迫代替她去往敵國和親纹坐。 傳聞我的和親對象是個(gè)殘疾皇子枝冀,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評論 2 360

推薦閱讀更多精彩內(nèi)容

  • 關(guān)于Mongodb的全面總結(jié) MongoDB的內(nèi)部構(gòu)造《MongoDB The Definitive Guide》...
    中v中閱讀 31,947評論 2 89
  • MYSQL 基礎(chǔ)知識 1 MySQL數(shù)據(jù)庫概要 2 簡單MySQL環(huán)境 3 數(shù)據(jù)的存儲和獲取 4 MySQL基本操...
    Kingtester閱讀 7,820評論 5 116
  • 我不需要你們的理解果漾,我希望就可以這樣自生自滅球切!
    此情唯一閱讀 266評論 0 0
  • 某一天,心情低落绒障,正好那一縷陽光射入吨凑,有人說,陽光都眷顧著我户辱,于是鸵钝,朋友說我早要擋住他,可是庐镐,始終未能如愿恩商,他還是...
    梅子香閱讀 364評論 0 0