https://zhuanlan.zhihu.com/p/213814000
## **一、加鎖的目的是什么?**
在我們了解數據庫鎖之前,首先我們必須要明白加鎖的目的是為了解決什么問題,如果你還不清楚的話,那么從現在起你應該知道,數據庫的鎖是為了解決事務的隔離性問題,為了讓事務之間相互不影響,每個事務進行操作的時候都會對數據加上一把特有的鎖,防止其他事務同時操作數據。如果你想一個人靜一靜,不被別人打擾,那么請在你的房門上加上一把鎖。
## **二、鎖實是基于什么實現的?**
為了后面大家后面對鎖理解的更透徹,所以務必要對此進行說明,鎖是基于什么實現的,你現實生活中家里的鎖是基于門來實現的,那么數據庫的鎖又是基于什么實現的呢? 那么我在這里可以告訴你,**數據庫里面的鎖是基于索引實現的**,在Innodb中我們的鎖都是作用在索引上面的,當我們的SQL命中索引時,那么鎖住的就是命中條件內的索引節點(行鎖),如果沒有命中索引的話,那我們鎖的就是整個索引樹(表鎖),如下圖一下鎖住的是整棵樹還是某幾個節點,完全取決于你的條件是否有命中到對應的索引節點。
**innodb索引結構圖(B+ tree):**

## **三、鎖的分類。**
數據庫里有的鎖有很多種,為了方面理解,所以我根據其相關性"人為"的對鎖進行了一個分類,分別如下
基于鎖的屬性分類:共享鎖、排他鎖。
基于鎖的粒度分類:表鎖、行鎖、記錄鎖、間隙鎖、臨鍵鎖。
基于鎖的狀態分類:意向共享鎖、意向排它鎖。
## **1、屬性鎖**
### **共享鎖(Share Lock)**
共享鎖又稱讀鎖,簡稱S鎖;當一個事務為數據加上讀鎖之后,其他事務只能對該數據加讀鎖,而不能對數據加寫鎖,直到所有的讀鎖釋放之后其他事務才能對其進行加持寫鎖。

共享鎖的特性主要是為了支持并發的讀取數據,讀取數據的時候不支持修改,避免出現重復讀的問題。
### **排他鎖(eXclusive Lock)**
排他鎖又稱寫鎖,簡稱X鎖;當一個事務為數據加上寫鎖時,其他請求將不能再為數據加任何鎖,直到該鎖釋放之后,其他事務才能對數據進行加鎖。

排他鎖的目的是在數據修改時候,不允許其他人同時修改,也不允許其他人讀取。避免了出現臟數據和臟讀的問題。
## **2、粒度鎖**
## **表鎖**
表鎖是指上鎖的時候鎖住的是整個表,當下一個事務訪問該表的時候,必須等前一個事務釋放了鎖才能進行對表進行訪問;
特點: 粒度大,加鎖簡單,容易沖突;

## **行鎖**
行鎖是指上鎖的時候鎖住的是表的某一行或多行記錄,其他事務訪問同一張表時,只有被鎖住的記錄不能訪問,其他的記錄可正常訪問;
特點:粒度小,加鎖比表鎖麻煩,不容易沖突,相比表鎖支持的并發要高;

## **記錄鎖(Record Lock)**
記錄鎖也屬于行鎖中的一種,只不過記錄鎖的范圍只是表中的某一條記錄,記錄鎖是說事務在加鎖后鎖住的只是表的某一條記錄。

**觸發條件:**精準條件命中,并且命中的條件字段是唯一索引;
**例如:**update user\_info set name=’張三’ where id=1 ,這里的id是唯一索引。
**記錄鎖的作用:**加了記錄鎖之后數據可以避免數據在查詢的時候被修改的重復讀問題,也避免了在修改的事務未提交前被其他事務讀取的臟讀問題。
## **間隙鎖(Gap Lock)**
間隙鎖屬于行鎖中的一種,間隙鎖是在事務加鎖后其鎖住的是表記錄的某一個區間,當表的相鄰ID之間出現空隙則會形成一個區間,遵循左開右閉原則。
比如下面的表里面的數據ID 為 1,4,5,7,10 ,那么會形成以下幾個間隙區間,-n-1區間,1-4區間,7-10區間,10-n區間 (-n代表負無窮大,n代表正無窮大)

**觸發條件:**范圍查詢并且查詢未命中記錄,查詢條件必須命中索引、間隙鎖只會出現在REPEATABLE\_READ(重復讀)的事務級別中。
**例如**:對應上圖的表執行select \* from user\_info where id>1 and id<4(這里的id是唯一索引) ,這個SQL查詢不到對應的記錄,那么此時會使用間隙鎖。
**間隙鎖作用**:防止幻讀問題,事務并發的時候,如果沒有間隙鎖,就會發生如下圖的問題,在同一個事務里,A事務的兩次查詢出的結果會不一樣。

## **臨鍵鎖(Next-Key Lock)**
臨鍵鎖也屬于行鎖的一種,并且它是INNODB的行鎖默認算法,總結來說它就是記錄鎖和間隙鎖的組合,臨鍵鎖會把查詢出來的記錄鎖住,同時也會把該范圍查詢內的所有間隙空間也會鎖住,再之它會把相鄰的下一個區間也會鎖住。
**例如:**下面表的數據執行 select \* from user\_info where id>1 and id<=13 for update ;
會鎖住ID為 1,5,10的記錄;同時會鎖住,1至5,5至10,10至15的區間。

**觸發條件:**范圍查詢并命中,查詢命中了索引。
**臨鍵鎖的作用:**結合記錄鎖和間隙鎖的特性,臨鍵鎖避免了在范圍查詢時出現臟讀、重復讀、幻讀問題。加了臨鍵鎖之后,在范圍區間內數據不允許被修改和插入。
## **3、狀態鎖**
狀態鎖包括意向共享鎖和意向排它鎖,把他們區分為狀態鎖的一個核心邏輯,是因為這兩個鎖都是都是描述是否可以對某一個表進行加表鎖的狀態。
**意向鎖的解釋**:當一個事務試圖對**整個表**進行加鎖(共享鎖或排它鎖)之前,首先需要獲得對應類型的意向鎖(意向共享鎖或意向共享鎖)
### **意向共享鎖**
當一個事務試圖對**整個表**進行加共享鎖之前,首先需要獲得這個表的意向共享鎖。
### **意向排他鎖**
當一個事務試圖對**整個表**進行加排它鎖之前,首先需要獲得這個表的意向排它鎖。
### **為什么我們需要意向鎖?**
意向鎖光從概念上可能有點難理解,所以我們有必要從一個案例來分析其作用,這里首先我們先要有一個概念那就是innodb加鎖的方式是基于索引,并且加鎖粒度是行鎖,然后我們來看下面的案例。
**第一步:**
事務A對user\_info表執行一個SQL:update user\_info set name =”張三” where id=6 加鎖情況如下圖;

**第二步:**
與此同時數據庫又接收到事務B修改數據的請求:SQL: update user\_info set name =”李四”;
1、因為事務B是對整個表進行修改操作,那么此SQL是需要對整個表進行加排它鎖的(update加鎖類型為排他鎖);
2、我們首先做的第一件事是先檢查這個表有沒有被別的事務鎖住,只要有事務對表里的任何一行數據加了共享鎖或排他鎖我們就無法對整個表加鎖(**排他鎖不能與任何屬性的鎖兼容**)**。**
3、因為INNODB鎖的機制是基于行鎖,那么這個時候我們會對整個索引每個節點一個個檢查,我們需要檢查每個節點是否被別的事務加了共享鎖或排它鎖。
4、最后檢查到索引ID為6的節點被事務A鎖住了,最后導致事務B只能等待事務A鎖的釋放才能進行加鎖操作。
**思考:**
在A事務的操作過程中,后面的每個需要對user\_info加持表鎖的事務都需要遍歷整個索引樹才能知道自己是否能夠進行加鎖,這種方式是不是太浪費時間和損耗數據庫性能了?
**所以就有了意向鎖的概念:** 如果當事務A加鎖成功之后就設置一個狀態告訴后面的人,已經有人對表里的行加了一個排他鎖了,你們不能對整個表加共享鎖或排它鎖了,那么后面需要對整個表加鎖的人只需要獲取這個狀態就知道自己是不是可以對表加鎖,避免了對整個索引樹的每個節點掃描是否加鎖,而這個狀態就是我們的意向鎖。