MySQL的又一神器-鎖,MySQL面試必備

原文連接:blog.ouyangsihai.cn >> MySQL的又一神器-鎖,MySQL面試必備html

1 什麼是鎖

1.1 鎖的概述

在生活中鎖的例子多的不能再多了,從古老的簡單的門鎖,到密碼鎖,再到如今的指紋解鎖,人臉識別鎖,這都是鎖的鮮明的例子,因此,咱們理解鎖應該是很是簡單的。java

再到MySQL中的鎖,對於MySQL來講,鎖是一個很重要的特性,數據庫的鎖是爲了支持對共享資源進行併發訪問,提供數據的完整性和一致性,這樣才能保證在高併發的狀況下,訪問數據庫的時候,數據不會出現問題。mysql

1.2 鎖的兩個概念

在數據庫中,lock和latch均可以稱爲鎖,可是意義卻不一樣。面試

Latch通常稱爲閂鎖(輕量級的鎖),由於其要求鎖定的時間必須很是短。若持續的時間長,則應用的性能會很是差,在InnoDB引擎中,Latch又能夠分爲mutex(互斥量)和rwlock(讀寫鎖)。其目的是用來保證併發線程操做臨界資源的正確性,而且一般沒有死鎖檢測的機制。算法

Lock的對象是事務,用來鎖定的是數據庫中的對象,如表、頁、行。而且通常lock的對象僅在事務commit或rollback後進行釋放(不一樣事務隔離級別釋放的時間可能不一樣)。sql

2 InnoDB存儲引擎中的鎖

2.1 鎖的粒度

在數據庫中,鎖的粒度的不一樣能夠分爲表鎖、頁鎖、行鎖,這些鎖的粒度之間也是會發生升級的,鎖升級的意思就是講當前鎖的粒度下降,數據庫能夠把一個表的1000個行鎖升級爲一個頁鎖,或者將頁鎖升級爲表鎖,下面分別介紹一下這三種鎖的粒度(參考自博客:http://www.javashuo.com/article/p-kqibkcao-kb.html)。數據庫

表鎖

表級別的鎖定是MySQL各存儲引擎中最大顆粒度的鎖定機制。該鎖定機制最大的特色是實現邏輯很是簡單,帶來的系統負面影響最小。因此獲取鎖和釋放鎖的速度很快。因爲表級鎖一次會將整個表鎖定,因此能夠很好的避免困擾咱們的死鎖問題。安全

固然,鎖定顆粒度大所帶來最大的負面影響就是出現鎖定資源爭用的機率也會最高,導致並大度大打折扣。微信

使用表級鎖定的主要是MyISAM,MEMORY,CSV等一些非事務性存儲引擎。markdown

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

頁鎖

頁級鎖定是MySQL中比較獨特的一種鎖定級別,在其餘數據庫管理軟件中也並非太常見。頁級鎖定的特色是鎖定顆粒度介於行級鎖定與表級鎖之間,因此獲取鎖定所須要的資源開銷,以及所能提供的併發處理能力也一樣是介於上面兩者之間。另外,頁級鎖定和行級鎖定同樣,會發生死鎖。
在數據庫實現資源鎖定的過程當中,隨着鎖定資源顆粒度的減少,鎖定相同數據量的數據所須要消耗的內存數量是愈來愈多的,實現算法也會愈來愈複雜。不過,隨着鎖定資源 顆粒度的減少,應用程序的訪問請求遇到鎖等待的可能性也會隨之下降,系統總體併發度也隨之提高。
使用頁級鎖定的主要是BerkeleyDB存儲引擎。

特色: 開銷和加鎖時間界於表鎖和行鎖之間;會出現死鎖;鎖定粒度界於表鎖和行鎖之間,併發度通常。

行鎖

行級鎖定最大的特色就是鎖定對象的粒度很小,也是目前各大數據庫管理軟件所實現的鎖定顆粒度最小的。因爲鎖定顆粒度很小,因此發生鎖定資源爭用的機率也最小,可以給予應用程序儘量大的併發處理能力而提升一些須要高併發應用系統的總體性能。

雖然可以在併發處理能力上面有較大的優點,可是行級鎖定也所以帶來了很多弊端。因爲鎖定資源的顆粒度很小,因此每次獲取鎖和釋放鎖須要作的事情也更多,帶來的消耗天然也就更大了。此外,行級鎖定也最容易發生死鎖。

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

比較表鎖咱們能夠發現,這兩種鎖的特色基本都是相反的,而從鎖的角度來講,表級鎖更適合於以查詢爲主,只有少許按索引條件更新數據的應用,如Web應用;而行級鎖則更適合於有大量按索引條件併發更新少許不一樣數據,同時又有併發查詢的應用,如一些在線事務處理(OLTP)系統。

MySQL 不一樣引擎支持的鎖的粒度

MySQL的又一神器-鎖,MySQL面試必備

2.2 鎖的類型

InnoDB存儲引擎中存在着不一樣類型的鎖,下面一一介紹一下。

S or X (共享鎖、排他鎖)

數據的操做其實只有兩種,也就是讀和寫,而數據庫在實現鎖時,也會對這兩種操做使用不一樣的鎖;InnoDB 實現了標準的行級鎖,也就是共享鎖(Shared Lock)和互斥鎖(Exclusive Lock)

  • 共享鎖(讀鎖)(S Lock),容許事務讀一行數據。
  • 排他鎖(寫鎖)(X Lock),容許事務刪除或更新一行數據。

IS or IX (共享、排他)意向鎖

爲了容許行鎖和表鎖共存,實現多粒度鎖機制,InnoDB存儲引擎支持一種額外的鎖方式,就稱爲意向鎖,意向鎖在 InnoDB 中是表級鎖,意向鎖分爲:

  • 意向共享鎖:表達一個事務想要獲取一張表中某幾行的共享鎖。
  • 意向排他鎖:表達一個事務想要獲取一張表中某幾行的排他鎖。

另外,這些鎖之間的並非必定能夠共存的,有些鎖之間是不兼容的,所謂兼容性就是指事務 A 得到一個某行某種鎖以後,事務 B 一樣的在這個行上嘗試獲取某種鎖,若是能當即獲取,則稱鎖兼容,反之叫衝突。

下面咱們再看一下這兩種鎖的兼容性。

  • S or X (共享鎖、排他鎖)的兼容性

MySQL的又一神器-鎖,MySQL面試必備

  • IS or IX (共享、排他)意向鎖的兼容性

MySQL的又一神器-鎖,MySQL面試必備

3 前面小結

這裏用一個思惟導圖把前面的概念作一個小結。

MySQL的又一神器-鎖,MySQL面試必備

4 一致性非鎖定讀和一致性鎖定讀

一致性鎖定讀(Locking Reads)

在一個事務中查詢數據時,普通的SELECT語句不會對查詢的數據進行加鎖,其餘事務仍能夠對查詢的數據執行更新和刪除操做。所以,InnoDB提供了兩種類型的鎖定讀來保證額外的安全性:

  • SELECT ... LOCK IN SHARE MODE
  • SELECT ... FOR UPDATE

    SELECT ... LOCK IN SHARE MODE: 對讀取的行添加S鎖,其餘事物能夠對這些行添加S鎖,若添加X鎖,則會被阻塞。

    SELECT ... FOR UPDATE: 會對查詢的行及相關聯的索引記錄加X鎖,其餘事務請求的S鎖或X鎖都會被阻塞。 當事務提交或回滾後,經過這兩個語句添加的鎖都會被釋放。 注意:只有在自動提交被禁用時,SELECT FOR UPDATE才能夠鎖定行,若開啓自動提交,則匹配的行不會被鎖定。

    一致性非鎖定讀

    一致性非鎖定讀(consistent nonlocking read) 是指InnoDB存儲引擎經過多版本控制(MVVC)讀取當前數據庫中行數據的方式。若是讀取的行正在執行DELETE或UPDATE操做,這時讀取操做不會所以去等待行上鎖的釋放。相反地,InnoDB會去讀取行的一個快照。因此,非鎖定讀機制大大提升了數據庫的併發性。

    來自網絡:侵權刪

一致性非鎖定讀是InnoDB默認的讀取方式,即讀取不會佔用和等待行上的鎖。在事務隔離級別READ COMMITTEDREPEATABLE READ下,InnoDB使用一致性非鎖定讀。

然而,對於快照數據的定義卻不一樣。在READ COMMITTED事務隔離級別下,一致性非鎖定讀老是讀取被鎖定行的最新一份快照數據。而在REPEATABLE READ事務隔離級別下,則讀取事務開始時的行數據版本

下面咱們經過一個簡單的例子來講明一下這兩種方式的區別。

首先建立一張表;

MySQL的又一神器-鎖,MySQL面試必備

插入一條數據;

insert into lock_test values(1);

查看隔離級別;

select @@tx_isolation;

MySQL的又一神器-鎖,MySQL面試必備

下面分爲兩種事務進行操做。

REPEATABLE READ事務隔離級別下;

MySQL的又一神器-鎖,MySQL面試必備

REPEATABLE READ事務隔離級別下,讀取事務開始時的行數據,因此當會話B修改了數據以後,經過之前的查詢,仍是能夠查詢到數據的。

READ COMMITTED事務隔離級別下;

MySQL的又一神器-鎖,MySQL面試必備

READ COMMITTED事務隔離級別下,讀取該行版本最新的一個快照數據,因此,因爲B會話修改了數據,而且提交了事務,因此,A讀取不到數據了。

5 行鎖的算法

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

  • Record Lock:單個行記錄上的鎖。
  • Gap Lock:間隙鎖,鎖定一個範圍,但不包含記錄自己。
  • Next-Key Lock:Gap Lock+Record Lock,鎖定一個範圍,而且鎖定記錄自己。

Record Lock:老是會去鎖住索引記錄,若是InnoDB存儲引擎表在創建的時候沒有設置任何一個索引,那麼這時InnoDB存儲引擎會使用隱式的主鍵來進行鎖定。

Next-Key Lock:結合了Gap Lock和Record Lock的一種鎖定算法,在Next-Key Lock算法下,InnoDB對於行的查詢都是採用這種鎖定算法。舉個例子10,20,30,那麼該索引可能被Next-Key Locking的區間爲:
MySQL的又一神器-鎖,MySQL面試必備

除了Next-Key Locking,還有Previous-Key Locking技術,這種技術跟Next-Key Lock正好相反,鎖定的區間是區間範圍和前一個值。一樣上述的值,使用Previous-Key Locking技術,那麼可鎖定的區間爲:
MySQL的又一神器-鎖,MySQL面試必備

不是全部索引都會加上Next-key Lock的,這裏有一種特殊的狀況,在查詢的列是惟一索引(包含主鍵索引)的狀況下,Next-key Lock會降級爲Record Lock

接下來,咱們來經過一個例子解釋一下。

CREATE TABLE test (
    x INT,
    y INT,
    PRIMARY KEY(x),    // x是主鍵索引
    KEY(y)    // y是普通索引
);
INSERT INTO test select 3, 2;
INSERT INTO test select 5, 3;
INSERT INTO test select 7, 6;
INSERT INTO test select 10, 8;

咱們如今會話A中執行以下語句;

SELECT * FROM test WHERE y = 3 FOR UPDATE

咱們分析一下這時候的加鎖狀況。

  • 對於主鍵x

MySQL的又一神器-鎖,MySQL面試必備

  • 輔助索引y

MySQL的又一神器-鎖,MySQL面試必備

用戶能夠經過如下兩種方式來顯示的關閉Gap Lock:

  • 將事務的隔離級別設爲 READ COMMITED。
  • 將參數innodb_locks_unsafe_for_binlog設置爲1。

Gap Lock的做用:是爲了阻止多個事務將記錄插入到同一個範圍內,設計它的目的是用來解決Phontom Problem(幻讀問題)。在MySQL默認的隔離級別(Repeatable Read)下,InnoDB就是使用它來解決幻讀問題。

幻讀:是指在同一事務下,連續執行兩次一樣的SQL語句可能致使不一樣的結果,第二次的SQL可能會返回以前不存在的行,也就是第一次執行和第二次執行期間有其餘事務往裏插入了新的行。

6 鎖帶來的問題

6.1 髒讀

髒讀: 在不一樣的事務下,當前事務能夠讀到另外事務未提交的數據。另外咱們須要注意的是默認的MySQL隔離級別是REPEATABLE READ是不會發生髒讀的,髒讀發生的條件是須要事務的隔離級別爲READ UNCOMMITTED,因此若是出現髒讀,可能就是這種隔離級別致使的。

下面咱們經過一個例子看一下。
MySQL的又一神器-鎖,MySQL面試必備

從上面這個例子能夠看出,當咱們的事務的隔離級別爲READ UNCOMMITTED的時候,在會話A尚未提交時,會話B就可以查詢到會話A沒有提交的數據。

6.2 不可重複讀

不可重複讀: 是指在一個事務內屢次讀取同一集合的數據,可是屢次讀到的數據是不同的,這就違反了數據庫事務的一致性的原則。可是,這跟髒讀仍是有區別的,髒讀的數據是沒有提交的,可是不可重複讀的數據是已經提交的數據。

咱們經過下面的例子來看一下這種問題的發生。

MySQL的又一神器-鎖,MySQL面試必備

從上面的例子能夠看出,在A的一次會話中,因爲會話B插入了數據,致使兩次查詢的結果不一致,因此就出現了不可重複讀的問題。

咱們須要注意的是不可重複讀讀取的數據是已經提交的數據,事務的隔離級別爲READ COMMITTED,這種問題咱們是能夠接受的。

若是咱們須要避免不可重複讀的問題的發生,那麼咱們可使用Next-Key Lock算法(設置事務的隔離級別爲READ REPEATABLE)來避免,在MySQL中,不可重複讀問題就是Phantom Problem,也就是幻像問題

6.3 丟失更新

丟失更新:指的是一個事務的更新操做會被另一個事務的更新操做所覆蓋,從而致使數據的不一致。在當前數據庫的任何隔離級別下都不會致使丟失更新問題,要出現這個問題,在多用戶計算機系統環境下有可能出現這種問題。

如何避免丟失更新的問題呢,咱們只須要讓事務的操做變成串行化,不要並行執行就能夠。

咱們通常使用SELECT ... FOR UPDATE語句,給操做加上一個排他X鎖。

6.4 小結

這裏咱們作一個小結,主要是在不一樣的事務的隔離級別下出現的問題的對照,這樣就更加清晰了。

MySQL的又一神器-鎖,MySQL面試必備

文章有不當之處,歡迎指正,若是喜歡微信閱讀,你也可×××學java`,獲取優質學習資源。
MySQL的又一神器-鎖,MySQL面試必備

相關文章
相關標籤/搜索