MySQL中的悲觀鎖和樂觀鎖

悲觀鎖(Pessimistic Lock)和樂觀鎖(Optimistic Lock)是數(shù)據(jù)庫系統(tǒng)中并發(fā)控制主要采用的技術(shù)手段逾条。針對(duì)不同的業(yè)務(wù)場(chǎng)景操禀,應(yīng)該選用不同的并發(fā)控制方式兽掰。不要把它們和數(shù)據(jù)庫中提供的鎖機(jī)制(行鎖魁巩、表鎖暴心、排他鎖、共享鎖)混為一談浩嫌。

Pessimistic Lock

概述

悲觀鎖(Pessimistic Lock)檐迟,顧名思義,就是很悲觀码耐,每次去拿數(shù)據(jù)的時(shí)候都認(rèn)為別人會(huì)修改追迟,所以每次在拿數(shù)據(jù)的時(shí)候都會(huì)上鎖,這樣別人想拿這個(gè)數(shù)據(jù)就會(huì)block直到它拿到鎖伐坏。假定會(huì)發(fā)生并發(fā)沖突怔匣,屏蔽一切可能違反數(shù)據(jù)完整性的操作。

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

特性

  • 需要依靠數(shù)據(jù)庫中的鎖機(jī)制來實(shí)現(xiàn)剿骨,即通過常用的select ... for update操作來實(shí)現(xiàn)悲觀鎖。
  • 需要開啟事務(wù)埠褪,在事務(wù)中實(shí)現(xiàn)鎖機(jī)制浓利。
  • 可以最大程度的保證數(shù)據(jù)操作的獨(dú)占性。
  • select for update語句中所有掃描過的行都會(huì)被鎖上钞速,這一點(diǎn)很容易造成問題贷掖。如果用悲觀鎖請(qǐng)確保用到了索引,否則造成鎖表渴语。
  • 長(zhǎng)事務(wù)中的鎖等待苹威,會(huì)導(dǎo)致其他用戶長(zhǎng)時(shí)間無法操作。
  • 主要用于數(shù)據(jù)爭(zhēng)用激烈的環(huán)境驾凶,以及發(fā)生并發(fā)沖突時(shí)使用鎖保護(hù)數(shù)據(jù)的成本要低于回滾事務(wù)的成本的環(huán)境中牙甫。

Optimistic Lock

樂觀鎖,又稱樂觀并發(fā)控制(Optimistic Concurrency Control)调违,樂觀地認(rèn)為不會(huì)發(fā)生并發(fā)問題窟哺,只在提交更新操作時(shí)檢查是否違反數(shù)據(jù)的一致性。

概述

樂觀鎖在數(shù)據(jù)庫中的實(shí)現(xiàn)完全是邏輯性的技肩,不需要數(shù)據(jù)庫提供特殊的支持且轨。一般的做法是在數(shù)據(jù)表中增加一個(gè)字段(版本號(hào)或者時(shí)間戳),作為數(shù)據(jù)的版本標(biāo)識(shí)。讀取數(shù)據(jù)時(shí)旋奢,將版本號(hào)一同讀出;之后更新數(shù)據(jù)時(shí)黄绩,加入版本號(hào)條件,更新成功就將版本號(hào)加1玷过。樂觀鎖的重點(diǎn)在于爽丹,更新數(shù)據(jù)時(shí),加入版本號(hào)匹配條件辛蚊,將數(shù)據(jù)的版本與數(shù)據(jù)表中對(duì)應(yīng)記錄的當(dāng)前版本進(jìn)行匹配更新粤蝎,如果數(shù)據(jù)的版本號(hào)等于數(shù)據(jù)表的當(dāng)前版本號(hào),則獲取鎖成功袋马,也就是更新成功初澎;否則,更新失敗虑凛,需要回滾整個(gè)業(yè)務(wù)操作碑宴。Java中的atomic包就是樂觀鎖的一種實(shí)現(xiàn),AtomicInteger 通過CAS(Compare And Set)操作實(shí)現(xiàn)線程安全的自增桑谍。

實(shí)現(xiàn)機(jī)制

在數(shù)據(jù)庫中延柠,update同一行的情況是不允許并發(fā)的,即數(shù)據(jù)庫每次執(zhí)行一條update語句時(shí)會(huì)獲取被update行的寫鎖锣披,直到這一行被成功更新后才釋放贞间。因此在業(yè)務(wù)操作進(jìn)行前獲取需要鎖的數(shù)據(jù)的當(dāng)前版本號(hào),然后實(shí)際更新數(shù)據(jù)時(shí)雹仿,以版本號(hào)作為條件增热,再次對(duì)比版本號(hào)確認(rèn)與之前獲取的相同,并更新版本號(hào)胧辽,即可確認(rèn)沒有發(fā)生并發(fā)的修改峻仇。如果更新失敗即可認(rèn)為老版本的數(shù)據(jù)已經(jīng)被并發(fā)修改掉了,此時(shí)認(rèn)為獲取鎖失敗票顾,需要回滾整個(gè)業(yè)務(wù)操作并可根據(jù)需要重試整個(gè)過程础浮。

特性

  • 不需要依靠數(shù)據(jù)庫中的鎖機(jī)制來實(shí)現(xiàn),但需要在表中新增一個(gè)版本號(hào)奠骄,在邏輯上實(shí)現(xiàn)豆同。
  • 無論是否開啟事務(wù),都可以在邏輯上實(shí)現(xiàn)樂觀鎖含鳞。
  • 樂觀鎖在不發(fā)生取鎖失敗的情況下開銷比悲觀鎖小影锈,但是一旦發(fā)生失敗回滾開銷則比較大,因此適合用在取鎖失敗概率比較小的場(chǎng)景,可以提升系統(tǒng)并發(fā)性能鸭廷。

示例

悲觀鎖

用數(shù)據(jù)庫來演示悲觀鎖枣抱,首先悲觀鎖是必須用到數(shù)據(jù)庫的事務(wù)機(jī)制,其次要注意查詢條件字段必須是索引字段辆床,否則會(huì)造成鎖表佳晶。

  1. 開啟事務(wù)

begin

  1. 執(zhí)行for update操作。

select * from t_logs where id = '2' for update;

  1. 不要執(zhí)行commit操作讼载,為了模仿并發(fā)操作轿秧。

在Navicat中開啟另一個(gè)會(huì)話窗口

  1. 開啟事務(wù)

begin

  1. 執(zhí)行update操作

update t_logs set action = '測(cè)試用例' where id = '2';

  1. 如果不執(zhí)行上一個(gè)會(huì)話的commit操作,會(huì)發(fā)現(xiàn)此會(huì)話一直處于block狀態(tài)咨堤。
  2. 執(zhí)行上一個(gè)會(huì)話的commit操作菇篡,提交數(shù)據(jù)。
  3. 執(zhí)行此會(huì)話的commit操作一喘。

樂觀鎖

商品的庫存量是固定的驱还,保證商品數(shù)量不超賣, 需要保證數(shù)據(jù)一致性:用樂觀鎖來保證某個(gè)人點(diǎn)擊秒殺后系統(tǒng)中查出來的庫存量和實(shí)際扣減庫存時(shí)庫存量是一致的凸克。

  1. 商品表
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 '庫存數(shù)量',
  `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='商品庫存表';
  1. POJO類
@Getter
@Setter
@ToString
public class ProductStock {

    private Long productId; //商品id

    private Integer number; //庫存量

}
  1. 鎖實(shí)現(xiàn)
public boolean updateStock(Long productId) {
        int updateCnt = 0;
        while (updateCnt == 0) {
            ProductStock product = query("SELECT * FROM tb_product_stock WHERE product_id=#{productId}", productId);
            if (product.getNumber() > 0) {
                // 確保庫存不會(huì)減為負(fù)數(shù)
                updateCnt = update("UPDATE tb_product_stock SET number=number-1 WHERE product_id=#{productId} AND number>=1", productId);
                if(updateCnt > 0){    //更新庫存成功
                    return true;
                }
            } else {    //賣完
                return false;
            }
        }
        return false;
    }

UPDATE 語句的WHERE 條件字句上需要建索引议蟆,避免全表掃描。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末萎战,一起剝皮案震驚了整個(gè)濱河市咪鲜,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌撞鹉,老刑警劉巖疟丙,帶你破解...
    沈念sama閱讀 212,718評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異鸟雏,居然都是意外死亡享郊,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門孝鹊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來炊琉,“玉大人,你說我怎么就攤上這事又活√洌” “怎么了?”我有些...
    開封第一講書人閱讀 158,207評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵柳骄,是天一觀的道長(zhǎng)团赏。 經(jīng)常有香客問我,道長(zhǎng)耐薯,這世上最難降的妖魔是什么舔清? 我笑而不...
    開封第一講書人閱讀 56,755評(píng)論 1 284
  • 正文 為了忘掉前任丝里,我火速辦了婚禮,結(jié)果婚禮上体谒,老公的妹妹穿的比我還像新娘杯聚。我一直安慰自己,他們只是感情好抒痒,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,862評(píng)論 6 386
  • 文/花漫 我一把揭開白布幌绍。 她就那樣靜靜地躺著,像睡著了一般故响。 火紅的嫁衣襯著肌膚如雪纷捞。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,050評(píng)論 1 291
  • 那天被去,我揣著相機(jī)與錄音,去河邊找鬼奖唯。 笑死惨缆,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的丰捷。 我是一名探鬼主播坯墨,決...
    沈念sama閱讀 39,136評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼病往!你這毒婦竟也來了捣染?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,882評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤停巷,失蹤者是張志新(化名)和其女友劉穎耍攘,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體畔勤,經(jīng)...
    沈念sama閱讀 44,330評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蕾各,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,651評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了庆揪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片式曲。...
    茶點(diǎn)故事閱讀 38,789評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖缸榛,靈堂內(nèi)的尸體忽然破棺而出吝羞,到底是詐尸還是另有隱情,我是刑警寧澤内颗,帶...
    沈念sama閱讀 34,477評(píng)論 4 333
  • 正文 年R本政府宣布钧排,位于F島的核電站,受9級(jí)特大地震影響均澳,放射性物質(zhì)發(fā)生泄漏卖氨。R本人自食惡果不足惜会烙,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,135評(píng)論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望筒捺。 院中可真熱鬧柏腻,春花似錦、人聲如沸系吭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽肯尺。三九已至沃缘,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間则吟,已是汗流浹背槐臀。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評(píng)論 1 267
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留氓仲,地道東北人水慨。 一個(gè)月前我還...
    沈念sama閱讀 46,598評(píng)論 2 362
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像敬扛,于是被迫代替她去往敵國(guó)和親晰洒。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,697評(píng)論 2 351