日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

漫谈悲观锁乐观锁

發(fā)布時間:2023/12/20 编程问答 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 漫谈悲观锁乐观锁 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

關(guān)注公眾號【高性能架構(gòu)探索】,后臺回復(fù)【pdf】,免費獲取計算機(jī)必備經(jīng)典書籍

概念

悲觀鎖(Pessimistic?Lock)

總是假設(shè)最壞的情況,每次拿數(shù)據(jù)的時候,都認(rèn)為別人也會修改,所以每次都會加鎖。當(dāng)要對數(shù)據(jù)庫中的一條數(shù)據(jù)進(jìn)行修改的時候,為了避免同時被其他人修改,最好的辦法就是直接對該數(shù)據(jù)進(jìn)行加鎖以防止并發(fā)。這種借助數(shù)據(jù)庫鎖機(jī)制,在修改數(shù)據(jù)之前先鎖定,再修改的方式被稱之為悲觀并發(fā)控制【Pessimistic?Concurrency?Control,縮寫“PCC”,又名“悲觀鎖”】。

悲觀鎖,正如其名,具有強(qiáng)烈的獨占和排他特性。它指的是對數(shù)據(jù)被外界(包括本系統(tǒng)當(dāng)前的其他事務(wù),以及來自外部系統(tǒng)的事務(wù)處理)修改持保守態(tài)度。因此,在整個數(shù)據(jù)處理過程中,將數(shù)據(jù)處于鎖定狀態(tài)。悲觀鎖的實現(xiàn),往往依靠數(shù)據(jù)庫提供的鎖機(jī)制(也只有數(shù)據(jù)庫層提供的鎖機(jī)制才能真正保證數(shù)據(jù)訪問的排他性,否則,即使在本系統(tǒng)中實現(xiàn)了加鎖機(jī)制,也無法保證外部系統(tǒng)不會修改數(shù)據(jù))。

樂觀鎖(Optimistic?Locking)

樂觀鎖是相對悲觀鎖而言的,總是假設(shè)最好的情況,每次拿數(shù)據(jù)的時候,都認(rèn)為別人不會修改。但是在更新數(shù)據(jù)的時候,會判斷再次期間有沒有人去修改這個數(shù)據(jù),如果發(fā)現(xiàn)被修改了即產(chǎn)生了沖突,則返回給用戶錯誤的信息,讓用戶決定如何去做。樂觀鎖適用于讀操作多的場景,這樣可以提高程序的吞吐量。

基本原理

實現(xiàn)方式

悲觀鎖

悲觀鎖的實現(xiàn)是依賴于數(shù)據(jù)庫提供的鎖機(jī)制,流程如下:

  • 修改記錄前,對記錄加上排他鎖(exclusive?locking)

  • 如果加鎖失敗,說明這條數(shù)據(jù)正在被修改,那么當(dāng)前查詢要等待或者拋出異常,這由開發(fā)者決定

  • 如果加鎖成功,可以對這條數(shù)據(jù)修改了,事務(wù)完成解鎖

  • 加鎖修改期間,其他事務(wù)也想對這條記錄進(jìn)行操作時,都要等待或者直接拋出異常

  • 注意:在使用?mysql?Innodb?引擎實現(xiàn)悲觀鎖時,必須關(guān)閉?mysql?的自動提交屬性,因為?MySQL?默認(rèn)使用?autocommit?模式,也就是說,當(dāng)你執(zhí)行一個更新操作后,MySQL?會立刻將結(jié)果進(jìn)行提交。執(zhí)行:set?autocommit?=?0;

    樂觀鎖

    通常有兩種實現(xiàn)方式:

    1、版本號方式,由用戶來實現(xiàn)

    2、CAS(Compare?And?Set)

    下面以一個例子來理解悲觀鎖和樂觀鎖的機(jī)制

    A和B用戶最近都想吃豬肉脯,于是他們打開了購物網(wǎng)站,并且找到了同一家賣豬肉脯的店鋪。

    該店鋪的商品表goods和表數(shù)據(jù)如下:

    idnamenum
    1

    豬肉脯

    1
    2牛肉干1

    從表中可以看到豬肉脯目前的數(shù)量只有1個了。在不加鎖的情況下,如果A,B同時下單,就有可能導(dǎo)致超賣。

    悲觀鎖解決:

    利用悲觀鎖的解決思路是,我們認(rèn)為數(shù)據(jù)修改產(chǎn)生沖突的概率比較大,所以在更新之前,我們顯示的對要修改的記錄進(jìn)行加鎖,直到自己修改完再釋放鎖。加鎖期間只有自己可以進(jìn)行讀寫,其他事務(wù)只能讀不能寫。

    A下單前先給豬肉脯這行數(shù)據(jù)(id=1)加上悲觀鎖(行鎖)。此時這行數(shù)據(jù)只能A來操作,也就是只有A能買。B想買就必須一直等待。

    當(dāng)A買好后,B再想去買的時候會發(fā)現(xiàn)數(shù)量已經(jīng)為0,那么B看到后就會放棄購買。

    那么如何給豬肉脯也就是id=1這條數(shù)據(jù)加上悲觀鎖鎖呢?我們可以通過以下語句給id=1的這行數(shù)據(jù)加上悲觀鎖

    select?num?from?goods?where?id?=?1?for?update;

    通過開啟mysql的兩個會話來分析整個悲觀鎖的執(zhí)行過程

    1、事務(wù)A執(zhí)行命令給id?=?1的數(shù)據(jù)上悲觀鎖準(zhǔn)備更新數(shù)據(jù)

    //0.開始事務(wù)begin; //1.查詢出商品庫存信息select num from goods where id = 1 for update;//2.修改商品庫存為2update?goods?set?num?=?0?where?id?=?1;//3.提交事務(wù)commit;

    2、事務(wù)B也去給id?=?1的數(shù)據(jù)上悲觀鎖準(zhǔn)備更新數(shù)據(jù)

    //0.開始事務(wù)begin; //1.查詢出商品庫存信息select num from goods where id = 1 for update;

    事務(wù)B將會阻塞,一直在等待A釋放鎖,如果A長期不釋放鎖,那么最終事務(wù)B將會報錯(超時)

    3、當(dāng)事務(wù)A執(zhí)行完成后,事務(wù)B拿到了鎖,此時獲取到的商品數(shù)為0,那么B看到后,就知道沒有庫存了。

    樂觀鎖解決:

    版本號方式

    idnamenumversion
    1

    豬肉脯

    10
    2牛肉干10

    使用樂觀鎖的解決思路是,我們認(rèn)為數(shù)據(jù)修改產(chǎn)生的沖突的概率不大,多個事務(wù)在修改數(shù)據(jù)之前,先查出版本號,在修改的時候,把當(dāng)前版本號作為修改條件,只會有一個事務(wù)可以修改成功,其他事務(wù)則會失敗

    A和B同事將id?=?1的豬肉鋪的數(shù)據(jù)查出來,然后A先買,A將id?=?1的version=?1作為條件進(jìn)行數(shù)據(jù)更新,即數(shù)量-1,并且將版本號+1

    此時版本號變?yōu)?,A此時就完成了商品的購買。最后B開始賣,B也將id?=?1和version?=?0作為條件,進(jìn)行數(shù)據(jù)更新,但是更新完后發(fā)現(xiàn)更新的數(shù)據(jù)行數(shù)為0,此時說明已經(jīng)有人改動過數(shù)據(jù),此時就應(yīng)該提示用戶重新查看最新數(shù)據(jù)購買。

    1、A事務(wù)和B事務(wù)同時查找id?=?1?的數(shù)量和版本號

    select?num,?version?from?goods?where?id?= 1;//此時得到的值分別為1 和 0

    2、事務(wù)A進(jìn)行購買

    update?goods?set?num?=?num?-?1,?version?=?version?+?1?where?id?=?1;select?num,?version?from?goods?where?id?=?1;//此時得到的值為0和1

    3、事務(wù)B進(jìn)行購買

    update?goods?set?num?=?num?-?1,?version?=?version?+?1?where?id?=?1?and?version?=?1;//購買失敗

    CAS方式

    CAS是一種樂觀鎖實現(xiàn)方式,顧名思義就是先比較后更新。在對一個數(shù)據(jù)進(jìn)行更新前,先持有對這個數(shù)據(jù)原有值的備份。比如,要將a=2更新為a=3,在進(jìn)行更新前會比較此刻a是否為2.如果是2,才會進(jìn)行更新操作。當(dāng)多個線程嘗試使用CAS同時更新一個變量時,只有一個線程能夠成功,其余都是失敗。失敗的線程不會被掛起,而是被告知這次競爭失敗,并且可以再次嘗試。

    其代碼實現(xiàn)如下:

    int compare_and_swap(int* reg, int oldval, int newval) { ATOMIC(); int old_reg_val = *reg; if (old_reg_val == oldval) *reg = newval; END_ATOMIC(); return old_reg_val;}

    因為其實現(xiàn)方式與version方式類似,此處不再贅述

    使用場景

    切不可說某種鎖優(yōu)于另外一種鎖,每種鎖都有其適合的場景,一般來說,樂觀鎖適用于讀比較多,寫比較少的場景,這樣,省去了系統(tǒng)開銷,加大了系統(tǒng)吞吐量;而悲觀鎖適用于寫比較多的場景,如果在多寫的場景下用樂觀鎖,這就使得應(yīng)用層有大量的retry操作。

    優(yōu)缺點:

    悲觀鎖

    優(yōu)點:悲觀鎖利用數(shù)據(jù)庫中的鎖機(jī)制來實現(xiàn)數(shù)據(jù)變化的順序執(zhí)行,這是最有效的辦法

    缺點:一個事務(wù)用悲觀鎖對數(shù)據(jù)加鎖后,其他事務(wù)將不能對加鎖的數(shù)據(jù)進(jìn)行除了查詢以外的所有操作,如果該事務(wù)執(zhí)行的時間很長,那么其他事務(wù)將一直等待,那是比影響我們系統(tǒng)的吞吐量。

    樂觀鎖

    樂觀鎖不在數(shù)據(jù)庫上加鎖,任何事務(wù)都可以對數(shù)據(jù)進(jìn)行操作,這樣就避免了使用悲觀鎖造成的吞吐量下降。

    缺點:

    1、樂觀鎖是通過人為實現(xiàn)的,僅僅適用于我們自己業(yè)務(wù)中,如果有外部事務(wù)插入,勢必引起異常

    2、循環(huán)時間長開銷大

    自選CAS如果長時間不成功,會給CPU帶來非常大的執(zhí)行開銷

    3、只能保證一個共享變量的原子操作。

    總結(jié)

    以上是生活随笔為你收集整理的漫谈悲观锁乐观锁的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。