posts - 22, comments - 32, trackbacks - 0, articles - 73
            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

          InnoDB的鎖的機制探究

          Posted on 2021-07-12 15:38 為自己代言 閱讀(119) 評論(0)  編輯  收藏 所屬分類: 數據庫

          InnoDB的鎖

          InnoDB的行鎖:共享鎖、排他鎖、MDL鎖

          共享鎖:又稱讀鎖、S鎖。一個事務獲取一個數據行的共享鎖,其他事務能獲取該行對應的共享鎖,但不能獲得排他鎖;即一個事務在讀取一個數據行時,其他事務也可以讀,但不能對數據進行增刪改查。

          應用:

          1.自動提交模式下的select查詢,不加任何鎖,直接返回查詢結果

          2.通過select……lock in share mode在被讀取的行記錄或范圍上加一個讀鎖,其他事務可以讀,但是申請加寫鎖會被阻塞

          排他鎖:又稱寫鎖、X鎖。一個事務獲取了一個數據行的寫鎖,其他事務就不能再獲取該行的其他鎖,寫鎖優先級最高。

          應用:

          1.一些DML操作會對行記錄加寫鎖

          2.select for update會對讀取的行記錄上加一個寫鎖,其他任何事務都不能對鎖定的行加任何鎖,否則會被阻塞

          MDL鎖:MySQL5.5引入,用于保證表中元數據的信息。在會話A中,表開啟了查詢事務后,會自動獲得一個MDL鎖,會話B就不能執行任何DDL語句的操作

          行鎖實現方式

          InnoDB 行鎖是通過給索引上的索引項加鎖來實現的,這一點 MySQL 與 Oracle 不同,后者是 通過在數據塊中對相應數據行加鎖來實現的。InnoDB 這種行鎖實現特點意味著:只有通過 索引條件檢索數據,InnoDB 才使用行級鎖,否則,InnoDB 將使用表鎖! 在實際應用中,要特別注意 InnoDB 行鎖的這一特性,不然的話,可能導致大量的鎖沖突, 從而影響并發性能。

          行鎖的三種算法

          InnoDB 存儲引擎有三種行鎖的算法,其分別是:

          • Record Lock: 單個行記錄上的鎖
          • Gap Lock: 間隙鎖,鎖定一個范圍,但不包含記錄本身
          • Next-Key 鎖: Gap Lock + Record Lock,鎖定一個范圍,并且會鎖定記錄本身

          RC模式下只采用Record Lock,RR模式下采用了Next-Key

          加鎖場景分析

          • 主鍵索引

          如果我們加鎖的行上存在主鍵索引,那么就會在這個主鍵索引上添加一個 Record Lock。

          • 輔助索引

          如果我們加鎖的行上存在輔助索引,那么我們就會在這行的輔助索引上添加 Next-Key Lock,并在這行之后的輔助索引上添加一個 Gap Lock

          輔助索引上的 Next-Key Lock 和 Gap Lock 都是針對 Repeatable Read 隔離模式存在的,這兩種鎖都是為了防止幻讀現象的發生。

          • 唯一的輔助索引

          這里有一個特殊情況,如果輔助索引是唯一索引的話,MySQL 會將 Next-Key Lock 降級為 Record Lock,只會鎖定當前記錄的輔助索引。

          如果唯一索引由多個列組成的,而我們只鎖定其中一個列的話,那么此時并不會進行鎖降級,還會添加 Next-Key Lock 和 Gap Lock。

          • Insert 語句

          在 InnoDB 存儲引擎中,對于 Insert 的操作,其會檢查插入記錄的下一條記錄是否被鎖定,若已經被鎖定,則不允許查詢。

          意向鎖

          意向鎖可以分為意向共享鎖(Intention Shared Lock, IS)和意向排他鎖(Intention eXclusive Lock, IX)。但它的鎖定方式和共享鎖和排他鎖并不相同,意向鎖上鎖只是表示一種“意向”,并不會真的將對象鎖住,讓其他事物無法修改或訪問。例如事物T1想要修改表test中的行r1,它會上兩個鎖:

          1. 在表test上意向排他鎖
          2. 在行r1上排他鎖

          事物T1在test表上上了意向排他鎖,并不代表其他事物無法訪問test了,它上的鎖只是表明一種意向,它將會在db中的test表中的某幾行記錄上上一個排他鎖。


          意向共享鎖 意向排他鎖 共享鎖 排他鎖
          意向共享鎖 兼容 兼容 兼容 不兼容
          意向排他鎖 兼容 兼容 不兼容 不兼容
          共享鎖 兼容 不兼容 兼容 不兼容
          排他鎖 不兼容 不兼容 不兼容 不兼容

          一致性非鎖定讀

          一致性非鎖定讀是指 InnoDB 存儲引擎通過行多版本控制(multi version)的方式來讀取當前執行時間數據庫中行的數據。具體來說就是如果一個事務讀取的行正在被鎖定,那么它就會去讀取這行數據之前的快照數據,而不會等待這行數據上的鎖釋放。這個讀取流程如圖1所示:

          圖1

          行的快照數據是通過undo段來實現的,而undo段用來回滾事務,所以快照數據本身沒有額外的開銷。此外,讀取快照數據時不需要上鎖的,因為沒有事務會對快照數據進行更改。

          MySQL 中并不是每種隔離級別都采用非一致性非鎖定讀的讀取模式,而且就算是采用了一致性非鎖定讀,不同隔離級別的表現也不相同。在 READ COMMITTED 和 REPEATABLE READ 這兩種隔離級別下,InnoDB存儲引擎都使用一致性非鎖定讀。但是對于快照數據,READ COMMITTED 隔離模式中的事務讀取的是當前行最新的快照數據,而 REPEATABLE READ 隔離模式中的事務讀取的是事務開始時的行數據版本。

          一致性鎖定讀

          在 InnoDB 存儲引擎中,select語句默認采取的是一致性非鎖定讀的情況,但是有時候我們也有需求需要對某一行記錄進行鎖定再來讀取,這就是一致性鎖定讀。

          InnoDB 對于select語句支持以下兩種鎖定讀:

          • select ... for update
          • select ... lock in share mode

          select ... for update會對讀取的記錄加一個X鎖,其他事務不能夠再來為這些記錄加鎖。select ... lock in share mode會對讀取的記錄加一個S鎖,其它事務能夠再為這些記錄加一個S鎖,但不能加X鎖。

          對于一致性非鎖定讀,即使行記錄上加了X鎖,它也是能夠讀取的,因為它讀取的是行記錄的快照數據,并沒有讀取行記錄本身。

          select ... for updateselect ... lock in share mode這兩個語句必須在一個事務中,當事務提交了,鎖也就釋放了。因此在使用這兩條語句之前必須先執行begin, start transaction,或者執行set autocommit = 0。

          InnoDB 在不同隔離級別下的一致性讀及鎖的差異

          consisten read //一致性讀
          share locks //共享鎖
          Exclusive locks //排他鎖
          


          讀未提交 讀已提交 可重復讀 串行化
          SQL 條件



          select 相等 None locks Consisten read/None lock Consisten read/None lock Share locks

          范圍 None locks Consisten read/None lock Consisten read/None lock Share Next-Key
          update 相等 Exclusive locks Exclusive locks Exclusive locks Exclusive locks

          范圍 Exclusive next-key Exclusive next-key Exclusive next-key Exclusive next-key
          Insert N/A Exclusive locks Exclusive locks Exclusive locks Exclusive locks
          Replace 無鍵沖突 Exclusive locks Exclusive locks Exclusive locks Exclusive locks

          鍵沖突 Exclusive next-key Exclusive next-key Exclusive next-key Exclusive next-key
          delete 相等 Exclusive locks Exclusive locks Exclusive locks Exclusive locks

          范圍 Exclusive next-key Exclusive next-key Exclusive next-key Exclusive next-key
          Select … from … Lock in share mode 相等 Share locks Share locks Share locks Share locks

          范圍 Share locks Share locks Exclusive next-key Exclusive next-key
          Select * from … For update 相等 Exclusive locks Exclusive locks Exclusive locks Exclusive locks

          范圍 Exclusive locks Exclusive locks Exclusive next-key Exclusive next-key
          Insert into … Select … innodb_locks_ unsafe_for_bi nlog=off Share Next-Key Share Next-Key Share Next-Key Share Next-Key
          (指源表鎖) innodb_locks_ unsafe_for_bi nlog=on None locks Consisten read/None lock Consisten read/None lock Share Next-Key
          create table … Select … innodb_locks_ unsafe_for_bi nlog=off Share Next-Key Share Next-Key Share Next-Key Share Next-Key
          (指源表鎖) innodb_locks_ unsafe_for_bi nlog=on None locks Consisten read/None lock Consisten read/None lock Share Next-Key
          在了解 InnoDB 鎖特性后,用戶可以通過設計和 SQL 調整等措施減少鎖沖突和死鎖,包括:
          • 盡量使用較低的隔離級別;
          • 精心設計索引,并盡量使用索引訪問數據,使加鎖更精確,從而減少鎖沖突的機會;
          • 選擇合理的事務大小, 小事務發生鎖沖突的幾率也更小;
          • 給記錄集顯示加鎖時,最好一次性請求足夠級別的鎖。比如要修改數據的話,最好直接申請排他鎖,而不是先申請共享鎖,修改時再請求排他鎖,這樣容易產生死鎖;
          • 不同的程序訪問一組表時,應盡量約定以相同的順序訪問各表,對一個表而言,盡可能以固定的順序存取表中的行。這樣可以大大減少死鎖的機會;
          • 盡量用相等條件訪問數據,這樣可以避免間隙鎖對并發插入的影響;
          • 不要申請超過實際需要的鎖級別;除非必須,查詢時不要顯示加鎖;
          • 對于一些特定的事務,可以使用表鎖來提高處理速度或減少死鎖的可能。

          參考資料

          1.https://dev.mysql.com/doc/refman/5.7/en/innodb-locking.html#innodb-intention-locks  mysql官網開發手冊

          2.《MySQL 技術內幕 – InnoDB 存儲引擎》

          3.《深入淺出MySQL》

          4.https://www.modb.pro/db/33873



          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 舞阳县| 宁化县| 通山县| 富民县| 惠来县| 博兴县| 青海省| 安庆市| 九寨沟县| 闸北区| 无棣县| 博兴县| 芮城县| 弋阳县| 湘阴县| 岗巴县| 伊宁市| 沁源县| 定兴县| 久治县| 察雅县| 乌兰县| 阳山县| 出国| 那坡县| 通城县| 万全县| 湘乡市| 秀山| 大冶市| 庄河市| 玛多县| 嫩江县| 简阳市| 峨山| 运城市| 靖宇县| 浦县| 阿拉尔市| 临漳县| 顺平县|