java編程——MySQL InnoDB引擎鎖的總結

為什麼要鎖

我們開的的各式各樣系統中,系統運行需要CPU、內存、I/O、磁盤等等資源。但除了硬資源外,還有最為重要的軟資源:

數據

當人們訪問操作我們的系統時,其實歸根是對數據的查看與生產。那麼對於同一份數據,如果多個用戶同時對它查看、修改時會出現什麼問題呢?這必然會帶來競爭,而為了控制併發的讀取、修改數據會對數據造成的不一致、錯亂等問題,數據庫引入了鎖的機制。

當然鎖的問題解決了併發訪問數據的問題,而不可避免的會對系統的性能產生負面影響。總結一下各種鎖的使用場景方便在實踐中更好的運用它從而提升系統性能。

鎖的種類

我們日常開發中用到最多的存儲引擎是Innodb 與 MyISAM兩種,而 Innodb 現在更多是首選,因此主要是對 Innodb 的說明,MyISAM 跟多是作為一個對比的角色。

java編程——MySQL InnoDB引擎鎖的總結

MySQL的鎖可以按照多種方式進行劃分,這裡使用最常用的兩種方式進行劃分:粒度與使用方式。

需要特別說明的是:樂觀鎖與悲觀鎖並不是數據庫中實現的鎖機制,是需要我們自己去實現的。它是一種思想,我們不僅僅可以用在MySQL中,其它地方有需要的也可以用到。而像悲觀鎖它也是利用數據庫現有的機制進行實現的。下面先根據不同分類對說明相關概念。

按使用方式

樂觀鎖 機制採取了更加寬鬆的加鎖機制。悲觀鎖大多數情況下依靠數據庫的鎖機制實現,以保證操作最大程度的獨佔性。但隨之而來的就是數據庫性能的大量開銷,特別是對長事務而言,這樣的開銷往往無法承受。相對悲觀鎖而言,樂觀鎖更傾向於開發運用。

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

InnoDB中加鎖

MyISAM 相關的鎖機制我就略過不總結了。

InnoDB 實現了兩種類型的行鎖,共享鎖(S)與排他鎖(X)。然後由於 InnoDB引擎又支持表級鎖,所以它內部又有意向共享鎖(IS)與意向排他鎖(IX)。這兩種表鎖,都是InnoDB內部自動處理,換句話說我們寫代碼是無法控制也不需要控制的。我們能控制的是S與X鎖。

為MySQL加鎖

在日常操作中,UPDATEINSERTDELETE InnoDB會自動給涉及的數據集加排他鎖,一般的 SELECT 一般是不加任何鎖的。我們可以使用以下方式顯示的為 SELECT 加鎖。

  • 共享鎖:select * from table_name where id =10 lock in share mode;
  • 排他鎖:select * from table_name where id=10 for update;

那麼什麼時候該用共享鎖什麼時候用排他鎖呢?

一般來講,共享鎖主要用在需要數據依存關係時來確認某行記錄是否存在,並確保沒有人對這個記錄進行UPDATE或者DELETE操作(包括加鎖的事物也只能讀)。簡單說就是大家都可以讀數據,但是無法修改(更新或者刪除),因此我認為::共享鎖也是悲觀鎖::的一種實現。

排他鎖是與共享鎖相對應,自身加排他鎖的事物能夠自己發起修改操作,其它事物無法再對該數據加共享或者排他鎖。

這裡需要注意上面說到的一點,由於InnoDB引擎是行鎖,不管我們在這條數據上加了共享鎖還是排他鎖,簡單的select語句依然可以使用的,因為默認在InnoDB中select是不加鎖的。

這裡還有一點,上圖中我們寫到一個 間隙鎖,這是什麼東西?比如:當我們用範圍條件而不是相等條件檢索數據,並請求共享或排他鎖時,InnoDB會給符合條件的已有數據的索引項加鎖;對於鍵值在條件範圍內但並不存在的記錄,叫做“間隙(GAP)”,InnoDB也會對這個“間隙”加鎖,這種鎖機制就是所謂的間隙鎖(Next-Key鎖)。它存在的主要目的有一個是為了解決幻讀問題,因為RR作為InnoDB的默認事物隔離級別,是存在幻讀問題的,而我們在實際操作中確沒有出現,就是因為這裡做了處理。

關於樂觀鎖是如何加鎖的,這個不同系統有不同的實現,簡單來說,對每一個數據維護一個版本號,每次讀取時把版本號讀取出來,更新時版本號+1。然後更新時將讀取的版本號作為條件,如果有其它事物更新了,那麼必然會導致版本號變化,因為本次更新不會成功。這種機制最大程度的保證了併發。

查看鎖情況

java編程——MySQL InnoDB引擎鎖的總結

上面的語句能夠展示當前系統鎖的情況,當系統鎖爭用比較嚴重的時候,Innodb_row_lock_waits 和 Innodb_row_lock_time_avg 的值會比較高。上面的數據是由於我做實驗導致的。大家可以檢查下自己的系統。

總結

  • 悲觀鎖與樂觀鎖是一種思想,而不是數據庫鎖機制的實現;
  • InnoDB的行銷是基於索引實現的,如果不通過索引訪問數據,InnoDB會使用表鎖;
  • 雖然根據標準InnoDB的默認事務隔離級別RR是存在幻讀,但是通過間隙鎖以及MVCC解決了幻讀的問題;
  • 間隙鎖的存在會導致併發插入問題,儘量減少範圍查詢;
  • InnoDB的索引設計非常重要。
java編程——MySQL InnoDB引擎鎖的總結


分享到:


相關文章: