MySQL 樂(lè)觀鎖與悲觀鎖

本質(zhì): 悲觀鎖和樂(lè)觀鎖都是一種概念和認(rèn)知。數(shù)據(jù)庫(kù)有java語(yǔ)言都有對(duì)應(yīng)的實(shí)現(xiàn)方式组力。

悲觀鎖

悲觀鎖(Pessimistic Lock)瘟斜,顧名思義,就是很悲觀术陶,每次去拿數(shù)據(jù)的時(shí)候都認(rèn)為別人會(huì)修改凑懂,所以每次在拿數(shù)據(jù)的時(shí)候都會(huì)上鎖,這樣別人想拿這個(gè)數(shù)據(jù)就會(huì)block直到它拿到鎖梧宫。

悲觀鎖:假定會(huì)發(fā)生并發(fā)沖突接谨,屏蔽一切可能違反數(shù)據(jù)完整性的操作。

SELECT ... LOCK IN SHARE MODE - 共享鎖

SELECT ... FOR UPDATE -排它鎖(悲觀鎖)

SELECT * FROM tb_product_stock WHERE product_id=101 FOR UPDATE -悲觀鎖塘匣,每次查詢數(shù)據(jù)時(shí)候都認(rèn)為會(huì)有其他人修改脓豪,都加鎖。

Java synchronized鎖 就屬于悲觀鎖的一種實(shí)現(xiàn)忌卤,每次線程要修改數(shù)據(jù)時(shí)都先獲得鎖扫夜,保證同一時(shí)刻只有一個(gè)線程能操作數(shù)據(jù),其他線程則會(huì)被block。

樂(lè)觀鎖

樂(lè)觀鎖(Optimistic Lock)笤闯,顧名思義堕阔,就是很樂(lè)觀,每次去拿數(shù)據(jù)的時(shí)候都認(rèn)為別人不會(huì)修改颗味,所以不會(huì)上鎖超陆,但是在提交更新的時(shí)候會(huì)判斷一下在此期間別人有沒(méi)有去更新這個(gè)數(shù)據(jù)。樂(lè)觀鎖適用于讀多寫(xiě)少的應(yīng)用場(chǎng)景浦马,這樣可以提高吞吐量时呀。

樂(lè)觀鎖:假設(shè)不會(huì)發(fā)生并發(fā)沖突,只在提交操作時(shí)檢查是否違反數(shù)據(jù)完整性晶默。

java樂(lè)觀鎖實(shí)現(xiàn) CAS鎖

數(shù)據(jù)庫(kù)樂(lè)觀鎖一般來(lái)說(shuō)有以下2種方式:

1.使用數(shù)據(jù)版本(Version)記錄機(jī)制實(shí)現(xiàn)谨娜,這是樂(lè)觀鎖最常用的一種實(shí)現(xiàn)方式。何謂數(shù)據(jù)版本荤胁?即為數(shù)據(jù)增加一個(gè)版本標(biāo)識(shí)瞧预,一般是通過(guò)為數(shù)據(jù)庫(kù)表增加一個(gè)數(shù)字類型的 “version” 字段來(lái)實(shí)現(xiàn)。當(dāng)讀取數(shù)據(jù)時(shí)仅政,將version字段的值一同讀出垢油,數(shù)據(jù)每更新一次,對(duì)此version值加一圆丹。當(dāng)我們提交更新的時(shí)候滩愁,判斷數(shù)據(jù)庫(kù)表對(duì)應(yīng)記錄的當(dāng)前版本信息與第一次取出來(lái)的version值進(jìn)行比對(duì),如果數(shù)據(jù)庫(kù)表當(dāng)前版本號(hào)與第一次取出來(lái)的version值相等辫封,則予以更新硝枉,否則認(rèn)為是過(guò)期數(shù)據(jù)。

UPDATE tb_product_stock SET number=number-1, version=version+1 WHERE product_id=#{productId} and version=#{version} AND number=#{number}?-樂(lè)觀鎖,每次取數(shù)據(jù)時(shí)候認(rèn)為別人不會(huì)修改倦微,只在提交更新時(shí)候?qū)Ρ葀ersion妻味,版本跟當(dāng)初取數(shù)據(jù)時(shí)候版本一致則更新成功,否則更新失敗欣福。提高吞吐量责球。缺點(diǎn): 高并發(fā)情況下由于并發(fā)更新頻繁,導(dǎo)致樂(lè)觀鎖頻繁更新失敗拓劝,處理效率不高

2.使用時(shí)間戳(timestamp)雏逾。樂(lè)觀鎖定的第二種實(shí)現(xiàn)方式和第一種差不多,同樣是在需要樂(lè)觀鎖控制的table中增加一個(gè)字段郑临,名稱無(wú)所謂栖博,字段類型使用時(shí)間戳(timestamp), 和上面的version類似,也是在更新提交的時(shí)候檢查當(dāng)前數(shù)據(jù)庫(kù)中數(shù)據(jù)的時(shí)間戳和自己更新前取到的時(shí)間戳進(jìn)行對(duì)比厢洞,如果一致則OK仇让,否則就是版本沖突典奉。


示例: 商品扣庫(kù)存

CREATE TABLE `tb_product_stock` (

`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增ID',

`product_id` bigint(32) NOT NULL COMMENT '商品ID',

`number` INT(8) NOT NULL DEFAULT 0 COMMENT '庫(kù)存數(shù)量',

`version` INT(8) NOT NULL DEFAULT 0 COMMENT '數(shù)據(jù)版本',

`create_time` DATETIME NOT NULL COMMENT '創(chuàng)建時(shí)間',

`modify_time` DATETIME NOT NULL COMMENT '更新時(shí)間',

PRIMARY KEY (`id`),

UNIQUE KEY `index_pid` (`product_id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='商品庫(kù)存表';

不考慮并發(fā)的情況下,更新庫(kù)存代碼如下:

// 更新庫(kù)存(不考慮并發(fā))

public boolean updateStockRaw(Long productId){

? ? ProductStock product = query("SELECT * FROM tb_product_stock WHERE product_id=#{productId}", productId);

? ? if (product.getNumber() > 0) {

? ? ? ? int updateCnt = update("UPDATE tb_product_stock SET number=number-1 WHERE product_id=#{productId}", productId);

? ? ? ? if(updateCnt > 0){? ? //更新庫(kù)存成功

? ? ? ? ? ? return true;

? ? ? ? ?}

? ? }

? ?return false;

?}

悲觀鎖

// 更新庫(kù)存(使用悲觀鎖)

public boolean updateStock(Long productId){

//先鎖定商品庫(kù)存記錄

ProductStock product = query("SELECT * FROM tb_product_stock WHERE product_id=#{productId} FOR UPDATE", productId);

if (product.getNumber() > 0) {

? ? int updateCnt = update("UPDATE tb_product_stock SET number=number-1 WHERE product_id=#{productId}", productId);

? ? if(updateCnt > 0){? ? //更新庫(kù)存成功?

? ? ? ? return true;

? ? }

?}

return false;

}

樂(lè)觀鎖

public boolean updateStock(Long productId){

int updateCnt = 0;

while (updateCnt == 0) {

? ? ProductStock product = query("SELECT * FROM tb_product_stock WHERE product_id=#{productId}", productId);

? ? ? ? // 首先讀取行記錄 version字段妹孙,然后通過(guò)樂(lè)觀鎖 帶著version字段去更新原紀(jì)錄秋柄,如果沒(méi)有并發(fā)更新則會(huì)成功更新获枝,如果有并發(fā)更新則會(huì)更新失敗蠢正,需要重試處理。

? ? ? ? updateCnt = update("UPDATE tb_product_stock SET number=number-1, version=version+1 WHERE product_id=#{productId} and version=#{version} AND number=#{number}", productId, product.getVersion(), product.getNumber());

? ? ? ? if(updateCnt > 0){? ? //更新庫(kù)存成功

? ? ? ? ? ? ?return true;

? ? ? ? ?} else {

? ? ? ? ? ? ?return false;

????????}

}

樂(lè)觀鎖與悲觀鎖的區(qū)別

樂(lè)觀鎖的思路一般是表中增加version版本字段省店,更新時(shí)where語(yǔ)句中增加版本的判斷嚣崭,算是一種CAS(Compare And Swep)操作,商品庫(kù)存場(chǎng)景中起到了版本控制的作用

悲觀鎖之所以是悲觀懦傍,在于他認(rèn)為本次操作會(huì)發(fā)生并發(fā)沖突雹舀,所以一開(kāi)始就對(duì)商品加上鎖(SELECT ... FOR UPDATE),然后就可以安心的做判斷和更新粗俱,因?yàn)檫@時(shí)候不會(huì)有別人更新這條商品庫(kù)存说榆。


數(shù)據(jù)庫(kù)樂(lè)觀鎖高并發(fā)性能不是很好,更好的解決方案:

分布式鎖: 1.Redis分布式鎖(AP模型) 2.zk分布式鎖 3.etcd分布式鎖(CP模型)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末寸认,一起剝皮案震驚了整個(gè)濱河市签财,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌偏塞,老刑警劉巖唱蒸,帶你破解...
    沈念sama閱讀 217,509評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異灸叼,居然都是意外死亡神汹,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門古今,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)屁魏,“玉大人,你說(shuō)我怎么就攤上這事捉腥∶テ矗” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,875評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵但狭,是天一觀的道長(zhǎng)披诗。 經(jīng)常有香客問(wèn)我,道長(zhǎng)立磁,這世上最難降的妖魔是什么呈队? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,441評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮唱歧,結(jié)果婚禮上宪摧,老公的妹妹穿的比我還像新娘粒竖。我一直安慰自己,他們只是感情好几于,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布蕊苗。 她就那樣靜靜地躺著,像睡著了一般沿彭。 火紅的嫁衣襯著肌膚如雪朽砰。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,365評(píng)論 1 302
  • 那天喉刘,我揣著相機(jī)與錄音瞧柔,去河邊找鬼。 笑死睦裳,一個(gè)胖子當(dāng)著我的面吹牛造锅,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播廉邑,決...
    沈念sama閱讀 40,190評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼哥蔚,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了蛛蒙?” 一聲冷哼從身側(cè)響起糙箍,我...
    開(kāi)封第一講書(shū)人閱讀 39,062評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎宇驾,沒(méi)想到半個(gè)月后倍靡,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,500評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡课舍,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評(píng)論 3 335
  • 正文 我和宋清朗相戀三年塌西,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片筝尾。...
    茶點(diǎn)故事閱讀 39,834評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡捡需,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出筹淫,到底是詐尸還是另有隱情站辉,我是刑警寧澤,帶...
    沈念sama閱讀 35,559評(píng)論 5 345
  • 正文 年R本政府宣布损姜,位于F島的核電站饰剥,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏摧阅。R本人自食惡果不足惜汰蓉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望棒卷。 院中可真熱鬧顾孽,春花似錦祝钢、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,779評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至测秸,卻和暖如春疤估,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背乞封。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,912評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工做裙, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留岗憋,地道東北人肃晚。 一個(gè)月前我還...
    沈念sama閱讀 47,958評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像仔戈,于是被迫代替她去往敵國(guó)和親关串。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評(píng)論 2 354

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