HOME 首頁(yè)
SERVICE 服務(wù)產(chǎn)品
XINMEITI 新媒體代運(yùn)營(yíng)
CASE 服務(wù)案例
NEWS 熱點(diǎn)資訊
ABOUT 關(guān)于我們
CONTACT 聯(lián)系我們
創(chuàng)意嶺
讓品牌有溫度、有情感
專注品牌策劃15年

    mysql樂觀鎖怎么實(shí)現(xiàn)(mysql樂觀鎖實(shí)現(xiàn)原理)

    發(fā)布時(shí)間:2023-04-19 10:10:53     稿源: 創(chuàng)意嶺    閱讀: 80        

    大家好!今天讓創(chuàng)意嶺的小編來(lái)大家介紹下關(guān)于mysql樂觀鎖怎么實(shí)現(xiàn)的問(wèn)題,以下是小編對(duì)此問(wèn)題的歸納整理,讓我們一起來(lái)看看吧。

    開始之前先推薦一個(gè)非常厲害的Ai人工智能工具,一鍵生成原創(chuàng)文章、方案、文案、工作計(jì)劃、工作報(bào)告、論文、代碼、作文、做題和對(duì)話答疑等等

    只需要輸入關(guān)鍵詞,就能返回你想要的內(nèi)容,越精準(zhǔn),寫出的就越詳細(xì),有微信小程序端、在線網(wǎng)頁(yè)版、PC客戶端

    官網(wǎng):https://ai.de1919.com

    創(chuàng)意嶺作為行業(yè)內(nèi)優(yōu)秀的企業(yè),服務(wù)客戶遍布全球各地,如需了解SEO相關(guān)業(yè)務(wù)請(qǐng)撥打電話175-8598-2043,或添加微信:1454722008

    本文目錄:

    mysql樂觀鎖怎么實(shí)現(xiàn)(mysql樂觀鎖實(shí)現(xiàn)原理)

    一、關(guān)于mySql 中樂觀鎖與讀已提交(事務(wù)隔離級(jí)別)的搭配使用問(wèn)題??!求大神帶飛!

    術(shù)式之后皆為邏輯,一切皆為需求和實(shí)現(xiàn)。希望此文能從需求、現(xiàn)狀和解決方式的角度幫大家理解隔離級(jí)別。

    隔離級(jí)別的產(chǎn)生

    在串型執(zhí)行的條件下,數(shù)據(jù)修改的順序是固定的、可預(yù)期的結(jié)果,但是并發(fā)執(zhí)行的情況下,數(shù)據(jù)的修改是不可預(yù)期的,也不固定,為了實(shí)現(xiàn)數(shù)據(jù)修改在并發(fā)執(zhí)行的情況下得到一個(gè)固定、可預(yù)期的結(jié)果,由此產(chǎn)生了隔離級(jí)別。

    所以隔離級(jí)別的作用是用來(lái)平衡數(shù)據(jù)庫(kù)并發(fā)訪問(wèn)與數(shù)據(jù)一致性的方法。

    事務(wù)的4種隔離級(jí)別

    READ UNCOMMITTED       未提交讀,可以讀取未提交的數(shù)據(jù)。READ COMMITTED         已提交讀,對(duì)于鎖定讀(select with for update 或者 for share)、update 和 delete 語(yǔ)句,                       InnoDB 僅鎖定索引記錄,而不鎖定它們之間的間隙,因此允許在鎖定的記錄旁邊自由插入新記錄。                       Gap locking 僅用于外鍵約束檢查和重復(fù)鍵檢查。REPEATABLE READ        可重復(fù)讀,事務(wù)中的一致性讀取讀取的是事務(wù)第一次讀取所建立的快照。SERIALIZABLE           序列化

    在了解了 4 種隔離級(jí)別的需求后,在采用鎖控制隔離級(jí)別的基礎(chǔ)上,我們需要了解加鎖的對(duì)象(數(shù)據(jù)本身&間隙),以及了解整個(gè)數(shù)據(jù)范圍的全集組成。

    數(shù)據(jù)范圍全集組成

    SQL 語(yǔ)句根據(jù)條件判斷不需要掃描的數(shù)據(jù)范圍(不加鎖);

    SQL 語(yǔ)句根據(jù)條件掃描到的可能需要加鎖的數(shù)據(jù)范圍;

    以單個(gè)數(shù)據(jù)范圍為例,數(shù)據(jù)范圍全集包含:(數(shù)據(jù)范圍不一定是連續(xù)的值,也可能是間隔的值組成)

    1. 數(shù)據(jù)已經(jīng)填充了整個(gè)數(shù)據(jù)范圍:(被完全填充的數(shù)據(jù)范圍,不存在數(shù)據(jù)間隙)

    • 整形,對(duì)值具有唯一約束條件的數(shù)據(jù)范圍 1~5 ,

      已有數(shù)據(jù)1、2、3、4、5,此時(shí)數(shù)據(jù)范圍已被完全填充;

    • 整形,對(duì)值具有唯一約束條件的數(shù)據(jù)范圍 1 和 5 ,

      已有數(shù)據(jù)1、5,此時(shí)數(shù)據(jù)范圍已被完全填充;

    • 2. 數(shù)據(jù)填充了部分?jǐn)?shù)據(jù)范圍:(未被完全填充的數(shù)據(jù)范圍,是存在數(shù)據(jù)間隙)

    • 整形的數(shù)據(jù)范圍 1~5 ,

      已有數(shù)據(jù) 1、2、3、4、5,但是因?yàn)闆]有唯一約束,

      所以數(shù)據(jù)范圍可以繼續(xù)被 1~5 的數(shù)據(jù)重復(fù)填充;

    • 整形,具有唯一約束條件的數(shù)據(jù)范圍 1~5 ,

      已有數(shù)據(jù) 2,5,此時(shí)數(shù)據(jù)范圍未被完全填充,還可以填充 1、3、4 ;

    • 3. 數(shù)據(jù)范圍內(nèi)沒有任何數(shù)據(jù)(存在間隙)

      如下:

    • 整形的數(shù)據(jù)范圍 1~5 ,數(shù)據(jù)范圍內(nèi)當(dāng)前沒有任何數(shù)據(jù)。

    • 在了解了數(shù)據(jù)全集的組成后,我們?cè)賮?lái)看看事務(wù)并發(fā)時(shí),會(huì)帶來(lái)的問(wèn)題。

      無(wú)控制的并發(fā)所帶來(lái)的問(wèn)題

      并發(fā)事務(wù)如果不加以控制的話會(huì)帶來(lái)一些問(wèn)題,主要包括以下幾種情況。

      1. 范圍內(nèi)已有數(shù)據(jù)更改導(dǎo)致的:

    • 更新丟失:當(dāng)多個(gè)事務(wù)選擇了同一行,然后基于最初選定的值更新該行時(shí),

      由于每個(gè)事物不知道其他事務(wù)的存在,最后的更新就會(huì)覆蓋其他事務(wù)所做的更新;

    • 臟讀: 一個(gè)事務(wù)正在對(duì)一條記錄做修改,這個(gè)事務(wù)完成并提交前,這條記錄就處于不一致狀態(tài)。

      這時(shí),另外一個(gè)事務(wù)也來(lái)讀取同一條記錄,如果不加控制,

      第二個(gè)事務(wù)讀取了這些“臟”數(shù)據(jù),并據(jù)此做了進(jìn)一步的處理,就會(huì)產(chǎn)生提交的數(shù)據(jù)依賴關(guān)系。

      這種現(xiàn)象就叫“臟讀”。

    • 2. 范圍內(nèi)數(shù)據(jù)量發(fā)生了變化導(dǎo)致:

    • 不可重復(fù)讀:一個(gè)事務(wù)在讀取某些數(shù)據(jù)后的某個(gè)時(shí)間,再次讀取以前讀過(guò)的數(shù)據(jù),

      卻發(fā)現(xiàn)其讀出的數(shù)據(jù)已經(jīng)發(fā)生了改變,或者某些記錄已經(jīng)被刪除了。

      這種現(xiàn)象就叫“不可重復(fù)讀”。

    • 幻讀:一個(gè)事務(wù)按相同的查詢條件重新讀取以前檢索過(guò)的數(shù)據(jù),

      卻發(fā)現(xiàn)其他事務(wù)插入了滿足其查詢條件的新數(shù)據(jù),這種現(xiàn)象稱為“幻讀”。

      可以簡(jiǎn)單的認(rèn)為滿足條件的數(shù)據(jù)量變化了。

    • 因?yàn)闊o(wú)控制的并發(fā)會(huì)帶來(lái)一系列的問(wèn)題,這些問(wèn)題會(huì)導(dǎo)致無(wú)法滿足我們所需要的結(jié)果。因此我們需要控制并發(fā),以實(shí)現(xiàn)我們所期望的結(jié)果(隔離級(jí)別)。

      MySQL 隔離級(jí)別的實(shí)現(xiàn)

      InnoDB 通過(guò)加鎖的策略來(lái)支持這些隔離級(jí)別。

      行鎖包含:

    • Record Locks

      索引記錄鎖,索引記錄鎖始終鎖定索引記錄,即使表中未定義索引,

      這種情況下,InnoDB 創(chuàng)建一個(gè)隱藏的聚簇索引,并使用該索引進(jìn)行記錄鎖定。

    • Gap Locks

      間隙鎖是索引記錄之間的間隙上的鎖,或者對(duì)第一條記錄之前或者最后一條記錄之后的鎖。

      間隙鎖是性能和并發(fā)之間權(quán)衡的一部分。

      對(duì)于無(wú)間隙的數(shù)據(jù)范圍不需要間隙鎖,因?yàn)闆]有間隙。

    • Next-Key Locks

      索引記錄上的記錄鎖和索引記錄之前的 gap lock 的組合。

      假設(shè)索引包含 10、11、13 和 20。

      可能的next-key locks包括以下間隔,其中圓括號(hào)表示不包含間隔端點(diǎn),方括號(hào)表示包含端點(diǎn):

    • (負(fù)無(wú)窮大, 10]    (10, 11]    (11, 13]    (13, 20]    (20, 正無(wú)窮大)        對(duì)于最后一個(gè)間隔,next-key將會(huì)鎖定索引中最大值的上方,

    • 左右滑動(dòng)進(jìn)行查看

      "上確界"偽記錄的值高于索引中任何實(shí)際值。

      上確界不是一個(gè)真正的索引記錄,因此,實(shí)際上,這個(gè) next-key 只鎖定最大索引值之后的間隙。

      基于此,當(dāng)獲取的數(shù)據(jù)范圍中,數(shù)據(jù)已填充了所有的數(shù)據(jù)范圍,那么此時(shí)是不存在間隙的,也就不需要 gap lock。

      對(duì)于數(shù)據(jù)范圍內(nèi)存在間隙的,需要根據(jù)隔離級(jí)別確認(rèn)是否對(duì)間隙加鎖。

      默認(rèn)的 REPEATABLE READ 隔離級(jí)別,為了保證可重復(fù)讀,除了對(duì)數(shù)據(jù)本身加鎖以外,還需要對(duì)數(shù)據(jù)間隙加鎖。

      READ COMMITTED 已提交讀,不匹配行的記錄鎖在 MySQL 評(píng)估了 where 條件后釋放。

      對(duì)于 update 語(yǔ)句,InnoDB 執(zhí)行 "semi-consistent" 讀取,這樣它會(huì)將最新提交的版本返回到 MySQL,

      以便 MySQL 可以確定該行是否與 update 的 where 條件相匹配。

      總結(jié)&延展:

      唯一索引存在唯一約束,所以變更后的數(shù)據(jù)若違反了唯一約束的原則,則會(huì)失敗。

      當(dāng) where 條件使用二級(jí)索引篩選數(shù)據(jù)時(shí),會(huì)對(duì)二級(jí)索引命中的條目和對(duì)應(yīng)的聚簇索引都加鎖;所以其他事務(wù)變更命中加鎖的聚簇索引時(shí),都會(huì)等待鎖。

      行鎖的增加是一行一行增加的,所以可能導(dǎo)致并發(fā)情況下死鎖的發(fā)生。

      例如,

      在 session A 對(duì)符合條件的某聚簇索引加鎖時(shí),可能 session B 已持有該聚簇索引的 Record Locks,而 session B 正在等待 session A 已持有的某聚簇索引的 Record Locks。

      session A 和 session B 是通過(guò)兩個(gè)不相干的二級(jí)索引定位到的聚簇索引。

      session A 通過(guò)索引 idA,session B通過(guò)索引 idB 。

      當(dāng) where 條件獲取的數(shù)據(jù)無(wú)間隙時(shí),無(wú)論隔離級(jí)別為 rc 或 rr,都不會(huì)存在間隙鎖。

      比如通過(guò)唯一索引獲取到了已完全填充的數(shù)據(jù)范圍,此時(shí)不需要間隙鎖。

      間隙鎖的目的在于阻止數(shù)據(jù)插入間隙,所以無(wú)論是通過(guò) insert 或 update 變更導(dǎo)致的間隙內(nèi)數(shù)據(jù)的存在,都會(huì)被阻止。

      rc 隔離級(jí)別模式下,查詢和索引掃描將禁用 gap locking,此時(shí) gap locking 僅用于外鍵約束檢查和重復(fù)鍵檢查(主要是唯一性檢查)。

      rr 模式下,為了防止幻讀,會(huì)加上 Gap Locks。

      事務(wù)中,SQL 開始則加鎖,事務(wù)結(jié)束才釋放鎖。

      就鎖類型而言,應(yīng)該有優(yōu)化鎖,鎖升級(jí)等,例如rr模式未使用索引查詢的情況下,是否可以直接升級(jí)為表鎖。

      就鎖的應(yīng)用場(chǎng)景而言,在回放場(chǎng)景中,如果確定事務(wù)可并發(fā),則可以考慮不加鎖,加快回放速度。

      鎖只是并發(fā)控制的一種粒度,只是一個(gè)很小的部分:

      從不同場(chǎng)景下是否需要控制并發(fā),(已知無(wú)交集且有序的數(shù)據(jù)的變更,MySQL 的 MTS 相同前置事務(wù)的多事務(wù)并發(fā)回放)

      并發(fā)控制的粒度,(鎖是一種邏輯粒度,可能還存在物理層和其他邏輯粒度或方式)

      相同粒度下的優(yōu)化,(鎖本身存在優(yōu)化,如IX、IS類型的優(yōu)化鎖)

      粒度加載的安全&性能(如獲取行鎖前,先獲取頁(yè)鎖,頁(yè)鎖在執(zhí)行獲取行鎖操作后即釋放,無(wú)論是否獲取成功)等多個(gè)層次去思考并發(fā)這玩意。

    二、樂觀鎖的一個(gè)實(shí)現(xiàn)細(xì)節(jié),求解答

    悲觀鎖就是數(shù)據(jù)庫(kù)里面鎖住 類似for update查詢

    樂觀鎖不是在數(shù)據(jù)庫(kù)端鎖住的

    而是程序控制的

    你說(shuō)的那Mybatis我不知道是什么

    但是樂觀鎖一般是這樣

    比如你數(shù)據(jù)庫(kù)中有一條記錄

    你可以給他加上一個(gè)版本號(hào)

    這樣

    如果同時(shí)有2個(gè)人查詢出那個(gè)數(shù)據(jù)要修改

    第一個(gè)人先查出來(lái)有事走了

    第二個(gè)人查出來(lái)給改了

    這時(shí)候你看

    第一個(gè)人查出來(lái)的數(shù)據(jù)版本號(hào)比如是1

    第二個(gè)人查出來(lái)也是1 但是他改了數(shù)據(jù)以后版本號(hào)變成2

    這時(shí)候第一個(gè)人回來(lái)了繼續(xù)修改數(shù)據(jù)

    他的版本號(hào)是1 比2低

    這時(shí)候就告訴他數(shù)據(jù)過(guò)期

    樂觀鎖大概就是這個(gè)意思

    是一種思路!

    三、Java如何實(shí)現(xiàn)對(duì)Mysql數(shù)據(jù)庫(kù)的行鎖

    下面通過(guò)一個(gè)例子來(lái)說(shuō)明

    場(chǎng)景如下:

    用戶賬戶有余額,當(dāng)發(fā)生交易時(shí),需要實(shí)時(shí)更新余額。這里如果發(fā)生并發(fā)問(wèn)題,那么會(huì)造成用戶余額和實(shí)際交易的不一致,這對(duì)公司和客戶來(lái)說(shuō)都是很危險(xiǎn)的。

    那么如何避免:

    網(wǎng)上查了下,有以下兩種方法:

    1、使用悲觀鎖

    當(dāng)需要變更余額時(shí),通過(guò)代碼在事務(wù)中對(duì)當(dāng)前需要更新的記錄設(shè)置for update行鎖,然后開始正常的查詢和更新操作

    這樣,其他的事務(wù)只能等待該事務(wù)完成后方可操作

    當(dāng)然要特別注意,如果使用了Spring的事務(wù)注解,需要配置一下:

    <!-- (事務(wù)管理)transaction manager, use JtaTransactionManager for global tx -->

    <bean id="transactionManager"

    class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

    <property name="dataSource" ref="dataSource" />

    </bean>

    <!-- 使用annotation定義事務(wù) -->

    <tx:annotation-driven transaction-manager="transactionManager" />

    在指定代碼處添加事務(wù)注解

    @Transactional

    @Override

    public boolean increaseBalanceByLock(Long userId, BigDecimal amount)

    throws ValidateException {

    long time = System.currentTimeMillis();

    //獲取對(duì)記錄的鎖定

    UserBalance balance = userBalanceDao.getLock(userId);

    LOGGER.info("[lock] start. time: {}", time);

    if (null == balance) {

    throw new ValidateException(

    ValidateErrorCode.ERRORCODE_BALANCE_NOTEXIST,

    "user balance is not exist");

    }

    boolean result = userBalanceDao.increaseBalanceByLock(balance, amount);

    long timeEnd = System.currentTimeMillis();

    LOGGER.info("[lock] end. time: {}", timeEnd);

    return result;

    }

    MyBatis中的鎖定方式,實(shí)際測(cè)試該方法確實(shí)可以有效控制,不過(guò)在大并發(fā)量的情況下,可能會(huì)有性能問(wèn)題吧

    <select id="getLock" resultMap="BaseResultMap" parameterType="java.lang.Long">

    <![CDATA[

    select * from user_balance where id=#{id,jdbcType=BIGINT} for update;

    ]]>

    </select>

    2、使用樂觀鎖

    這個(gè)方法也同樣可以解決場(chǎng)景中描述的問(wèn)題(我認(rèn)為比較適合并不頻繁的操作):

    設(shè)計(jì)表的時(shí)候增加一個(gè)version(版本控制字段),每次需要更新余額的時(shí)候,先獲取對(duì)象,update的時(shí)候根據(jù)version和id為條件去更新,如果更新回來(lái)的數(shù)量為0,說(shuō)明version已經(jīng)變更

    需要重復(fù)一次更新操作,如下:sql腳本

    update user_balance set Balance = #{balance,jdbcType=DECIMAL},Version = Version+1 where Id = #{id,jdbcType=BIGINT} and Version = #{version,jdbcType=BIGINT}

    這是一種不使用數(shù)據(jù)庫(kù)鎖的方法,解決方式也很巧妙。當(dāng)然,在大量并發(fā)的情況下,一次扣款需要重復(fù)多次的操作才能成功,還是有不足之處的。不知道還有沒有更好的方法。

    四、mysql 樂觀鎖怎么解決并發(fā)

    mysql的最大連接數(shù)默認(rèn)是100, 這個(gè)數(shù)值對(duì)于并發(fā)連接很多的數(shù)據(jù)庫(kù)應(yīng)用是遠(yuǎn)遠(yuǎn)不夠的,當(dāng)連接請(qǐng)求大于默認(rèn)連接數(shù)后,就會(huì)出現(xiàn)無(wú)法連接數(shù)據(jù)庫(kù)的錯(cuò)誤,因此我們需要把它適當(dāng)調(diào)大一些。

    調(diào)節(jié)方法為:

    1.linux服務(wù)器中:改my.cnf中的值就行了

    2.Windows服務(wù)器中(我用的):

    在文件“my.ini”中找到段 [mysqld],在其中添加一行

    max_connections=200 ### 200可以更改為想設(shè)置成的值.

    然后重啟"mysql"服務(wù)。

    /mysqladmin所在路徑/mysqladmin -uroot -p variables

    輸入root數(shù)據(jù)庫(kù)賬號(hào)的密碼后可看到

    | max_connections | 1000 |

    以上就是關(guān)于mysql樂觀鎖怎么實(shí)現(xiàn)相關(guān)問(wèn)題的回答。希望能幫到你,如有更多相關(guān)問(wèn)題,您也可以聯(lián)系我們的客服進(jìn)行咨詢,客服也會(huì)為您講解更多精彩的知識(shí)和內(nèi)容。


    推薦閱讀:

    mysql查詢優(yōu)化的幾種方法(mysql查詢優(yōu)化的幾種方法有哪些)

    MySQL數(shù)據(jù)庫(kù)恢復(fù)(mysql數(shù)據(jù)庫(kù)恢復(fù)到某個(gè)時(shí)間)

    girl排行榜(ohmygirl排名)

    抖音自媒體運(yùn)營(yíng)(抖音自媒體運(yùn)營(yíng)方案)

    牡丹江裝修公司哪家好誰(shuí)知道(牡丹江裝修公司口碑排行)