MySQL數據庫(十)——鎖

(一)鎖

當數據庫有併發事務的時候,可能會產生數據不一致,這時候需要一些機制來保證訪問次序,鎖機制就是這樣的一個機制。

1)隔離級別與鎖的關係

  1. 在ReadUncommitted級別下,讀取數據不需要加共享鎖,這樣就不會和被修改的數據上的排他鎖衝突。
  2. 在ReadCommitted級別下,讀操作需要加共享鎖,但是在語句執行完之後釋放共享鎖。
  3. 在RepeatableRead級別下,讀操作需要加共享鎖,但是在事務提交之前並不釋放共享鎖,即在事務執行完畢以後才釋放共享鎖。
  4. Serializable是限制性最強的隔離級別,因爲該級別鎖定整個範圍的鍵,並一直持有鎖,直到事務完成。

2)數據庫鎖粒度

在關係型數據庫中可以按照鎖的粒度把數據庫鎖分爲行級鎖(InnoDB引擎)、表級鎖(MyISAM引擎)和頁級鎖(BDB引擎)。

(1)行級鎖

行級鎖是MySQL中鎖定粒度最細的一種鎖,只針對當前操作的行進行加鎖。行級鎖能大大減少數據庫操作的衝突,加鎖粒度最小,單加鎖的開銷也最大。行級鎖分爲共享鎖和排他鎖。

特點:開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖衝突的概率最低,併發度最高。

(2)表級鎖

表級鎖是MySQL中鎖定粒度最大的一種鎖,表示對當前操作的整張表加鎖。實現簡單,資源消耗較少,被大部分MySQL引擎支持。最常使用的MyISAM和InnoDB都支持表級鎖定。表級鎖分爲表共享鎖(共享鎖)和表獨佔寫鎖(排他鎖)。

特點:開銷小,加鎖快;不會出現死鎖;鎖定粒度大,發出鎖衝突的概率最高,併發度低。

(3)頁級鎖

頁級鎖是MySQL中鎖定粒度介於行鎖和表鎖中間的一種鎖。表鎖速度快但衝突多,行鎖衝突少但速度慢,所以取了折中的頁級,一次鎖定相鄰的一組記錄。

特點:開銷和加鎖時間界於表鎖和行鎖之間;會出現死鎖;鎖定粒度介於表鎖和行鎖之間,併發度一般。

(4)對比

表鎖:開銷小、加鎖快、不會出現死鎖、鎖粒度較大、發生鎖衝突的概率比較大。

行鎖:開銷大、加鎖慢、會出現死鎖、鎖粒度較小、發生鎖衝突的概率較小。

頁鎖:開銷和加鎖時間界於表鎖和行鎖之間;會出現死鎖;鎖定粒度介於表鎖和行鎖之間,併發度一般。

3)數據庫鎖類別

從鎖的類別上來講,有共享鎖和排他鎖。

(1)共享鎖

共享鎖,又叫做讀鎖。當用戶要進行數據的讀取時,對數據加上共享鎖。共享鎖可以同時加上多個。

(2)排他鎖

排他鎖就叫做寫鎖。當用戶要進行數據的寫入時對數據加上排他鎖,排他鎖只可以加一個,和其他的共享鎖、排他鎖都相斥。

(二)引擎的鎖

1)MyISAM表鎖

  • MyISAM支持表鎖,不支持事務處理、不支持外鍵。
  • MyISAM併發比較簡單,只支持表鎖粒度,鎖的粒度較大。但是不會引起死鎖,它支持表共享地讀鎖和表互斥地寫鎖。
  • MyISAM表的讀操作:不會阻塞其他用戶對同一張表的讀操作,但是阻塞其他用戶對同一張表的寫操作。
  • MyISAM表的寫操作:會阻塞其他用戶對同一個表的讀、寫操作。
  • MyISAM的讀寫互斥、寫寫互斥,讀讀共享。
  • 粒度:可控制範圍(表、行)

2)InnoDB行鎖

  • InnoDB支持事務,支持外鍵,支持行級鎖,併發程度高。
  • InnoDB支持兩種類型的行鎖:
  • 1)共享鎖(S):允許一個事務去讀一行,組織其他事務獲取相同的數據集的排他鎖。
  • 2)排他鎖(X):允許獲得其他鎖的事務更新數據,組織其他事務獲取懸停數據集的共享鎖和排他鎖。
  • InnoDB中行鎖是通過給索引上的索引選項加鎖實現的,而不是給表中的行記錄進行加鎖。意味着如果表中的行不存在索引,InnoDB使用表鎖來實現。

InnoDB是基於索引來完成行鎖

例如select * from tab_with_index where id = 1 for update;

for tpdate可以根據條件來完成行鎖鎖定,並且id是有索引鍵的列,如果id不是索引鍵,那麼InnoDB將完成表鎖。

  • InnoDB會產生死鎖,例如:對錶tb1、tb2進行數據查詢,有兩個窗口進行操作,設置爲手動提交事務。
  • 窗口1:先查詢tb1,再查詢tb2,然後進行事務提交commit。
  • 窗口2:先查詢tb2,再查詢tb1,然後進行事務提交commit。

3)InnoDB存儲索引的鎖的算法

  1. Record lock:單個行記錄上的鎖
  2. Gap lock:間隙鎖,鎖定一個範圍,不包括記錄本身。
  3. Next-key lock:record+gap鎖定一個範圍,包含記錄本身。

4)相關補充

  1. InnoDB對於行的查詢使用Next-key lock。
  2. Next-locking keying爲了解決Phantom Problem幻讀問題。
  3. 當查詢的索引含有唯一屬性時,將next-key lock降級爲record key。
  4. Gap鎖設計的目的是爲了阻止多個事務將記錄插入到同一範圍內,這會導致幻讀問題的產生。
  5. 有兩種方式顯式關閉gap鎖:除了外鍵約束和唯一性檢查外,其餘情況僅使用record lock

A、將事務隔離級別設置爲RC。

B、將參數innodb_locks_unsafe_for_binlog設置爲1。

(三)數據庫死鎖

死鎖是指兩個或多個事務在同一資源上相互佔用,並請求鎖定對方的資源,從而導致惡性循環的現象。

1)常見的解決方法

  1. 如果不同程序會併發存取多個表,儘量約定以相同的順序訪問表,可以大大降低死鎖機會。
  2. 在同一個事務中,儘可能做到一次鎖定所需要的所有資源,減少死鎖產生概率。
  3. 對於非常容易產生死鎖的業務部分,可以嘗試使用升級鎖定粒度,通過表級鎖來減少死鎖產生的概率。
  4. 如果業務處理不好,可以使用分佈式事務鎖或者樂觀鎖。

2)樂觀鎖悲觀鎖

數據庫管理系統(DBMS)中的併發控制的任務是,確保在多個事務同時存取數據庫中同一數據時,不破壞事務的隔離性和統一性以及數據庫的統一性。樂觀鎖和悲觀鎖是兩種主要的技術手段。

(1)介紹

  • 悲觀鎖:假設一定會發生併發衝突,屏蔽一切可能違反數據完整性的操作。在查詢完數據的時候就把事務鎖起來,直到提交事務。

實現方式:使用數據庫中的鎖機制。

  • 樂觀鎖假設不會發生併發衝突,只在提交操作時檢查是否違反數據完整性。在修改數據的時候把事務鎖起來,通過version的方式來進行鎖定。

實現方式:一般使用版本號控制或者CAS算法實現。

(2)使用場景

樂觀鎖適用於寫比較少的情況下(多讀場景)。即衝突真的很少發生的時候,這樣可以省去鎖的開銷,加大系統的吞吐量。

但如果是多寫的情況,一般會經常產生衝突,這樣會導致上層應用會不斷的進行retry,這樣反而降低性能,所以在多寫的場景下使用悲觀鎖比較合適。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章