Mysql學習(七)InnoDB行鎖的原理分析

一條簡單的SQL語句加鎖分析

SQL1

select * from t1 where t1.id = 1;

問題:
上述SQL語句加鎖麼,加的是什麼鎖?
答案:
分析加鎖需要考慮當前的隔離級別,該語句在串行化下MVCC會降級成Lock-Based CC,是加鎖的,加的是讀鎖。在其他三種隔離級別下,由於MVCC的快照讀,所以是不加鎖的。

SQL2

delete from t1 where t1.id = 10;

同樣是上述問題,那這個答案就有很多了,首先得分析下面幾個事情

  • 前提一:id列是不是主鍵?
  • 前提二:當前系統的隔離級別是什麼?
  • 前提三:id列如果不是主鍵,那麼id列上有索引嗎?
  • 前提四:id列上如果有二級索引,那麼這個索引是唯一索引嗎?
  • 前提五:兩個SQL的執行計劃是什麼?索引掃描?全表掃描?

那麼根據這些前提分析一下在下列組合下是如何加鎖的

組合一:id主鍵+RC

在這裏插入圖片描述
id是主鍵時,此SQL只需要在id=10這條記錄上加寫鎖即可。

組合二:id唯一索引+RC

分析過程如下:
在這裏插入圖片描述
過程如下:

  1. 由於id是唯一索引時,where會走id列的索引進行過濾,在找到id=10的記錄後對唯一索引id=10的記錄加X鎖。
  2. 同時會回表查詢主鍵索引name=d的數據,並對name=d的數據也加上X鎖。

爲什麼唯一索引與主鍵索引都要加上鎖
試想一下,如果在刪除的過程中併發了一條更新語句,where用的是name作爲條件,那麼如果刪除操作沒有對主鍵索引加鎖,那麼更新語句是感知不到刪除語句的存在從而進行更新,這違背了同一記錄上的寫操作需要串行化執行的原則。

組合三:id非唯一索引+RC

分析過程如下:
在這裏插入圖片描述
過程如下:

  1. 對ID索引中符合條件的數據加上X鎖。
  2. 再把對應的主鍵索引上的數據也加上X鎖。

與組合二的區別在於ID是非唯一索引會對滿足條件的多條數據都加上X鎖。而組合二隻會對一條數據加上X鎖。

組合三:id無索引+RC

分析過程如下:
在這裏插入圖片描述
過程如下:

  1. 由於沒有索引,所以走的是全表掃描,所以會對主鍵索引上每一條記錄施加X鎖。

爲什麼會對所有記錄施加X鎖,而不是表鎖或者說符合條件的數據加X鎖呢
這是由於InnoDB的實現決定的,由於沒有索引, 無法在存儲引擎層過濾(執行計劃裏的Using Where),所以存儲引擎對每一條數據加鎖後返回給Sql Server進行過濾。而且實際實現中是有一些改進的,Sql Server在進行過濾的過程中對不滿足條件的數據會立即執行unlock_row方法,把不滿足條件的記錄放鎖(違背2PL的約束),這樣做,保證了最後只有滿足條件的數據持有X鎖。但是對每條數據加鎖的步驟是沒法省略的。

組合五:id主鍵+RR

與組合一是相同的

組合六:id唯一索引+RR

與組合二是相同的

組合七:id非唯一索引+RR

這一組合與RC級別區別就很大了。

  • RC級別下是允許幻讀的出現。
  • 而MySql在RR級別下是不允許幻讀的出現的。
    所以爲了解決幻讀的問題,InnoDB會施加間隙鎖來解決這一問題。
    分析圖如下:
    在這裏插入圖片描述
    過程如下:
  1. 首先對ID索引中符合條件的數據施加X鎖。
  2. 對符合條件施加X鎖的數據前後間隙施加間隙鎖。
  3. 對ID索引對應的主鍵索引的數據施加X鎖。
    施加了GAP鎖以後,數據的前後都不會插入新的數據,就可以保證兩次當前讀的結果完全一致。

組合八:ID無索引+RR級別

分析圖如下:
在這裏插入圖片描述
這一點就非常恐怖了,由於沒有索引所以會對全表加鎖,還會對所有的間隙加鎖。這個時候對於該表除了不加鎖的快照讀,其他所有的併發操作全部鎖死。
當然對於這一點與組合四類似,MySql也有自己的優化
組合四:id無索引, RC類似,就是所謂的semi-consistent read。semi-consistent read開啓的情況下,對於不滿足查詢條件的記錄,MySQL會提前放鎖。針對上面的這個用例,就是除了記錄[d,10],[g,10]之外,所有的記錄鎖都會被釋放,同時不加GAP鎖。semi-consistent read如何觸發:要麼是read committed隔離級別;要麼是Repeatable Read隔離級別,同時設置了 innodb_locks_unsafe_for_binlog 參數。

複雜SQL語句的行鎖分析

SQL如下
在這裏插入圖片描述
首先我們根據SQL分析一下這些條件,首先檢索條件中

  1. index key:puptime > 1 and puptime < 20這個條件確定了idx_t1_pu上面的值
  2. Index Filter:userid = ‘hdc’ 。此條件,可以在idx_t1_pu索引上進行過濾,但不屬於Index Key。
  3. Table Filter:comment is not NULL。此條件,在idx_t1_pu索引上無法過濾,只能在聚簇索引
    上過濾。

分析完這些後我們看一下加鎖情況
在這裏插入圖片描述

從圖中可以看出,在RR隔離級別下,由Index Key所確定的範圍,被加上了GAP鎖;
Index Filter鎖給定的條件 (userid = ‘hdc’)何時過濾,視MySQL的版本而定,在MySQL 5.6版本之前,不支持Index Condition Pushdown(ICP),因此Index Filter在MySQL Server層過濾,在5.6後支持了Index Condition Pushdown,則在index上過濾。
若不支持ICP,不滿足Index Filter的記錄,也需要加上記錄X鎖;若支持ICP,則不滿足IndexFilter的記錄,無需加記錄X鎖 (圖中,用紅色箭頭標出的X鎖,是否要加,視是否支持ICP而定); 而Table Filter對應的過濾條件,則在聚簇索引中讀取後,在MySQL Server層面過濾,因此聚簇索引上也需要X鎖。
最後,選取出了一條滿足條件的記錄[8,hdc,d,5,good],但是加鎖的數量,要遠遠大於滿足條件的
記錄數量。

結論:

  • 在RR隔離級別下,針對一個複雜的SQL,首先需要提取其where條件。
  • Index Key確定的範圍,需要加上GAP鎖;
  • Index Filter過濾條件,視MySQL版本是否支持ICP,若支持ICP,則不滿足Index Filter的記錄,不加X鎖,否則需要X鎖;
  • Table Filter過濾條件,無論是否滿足,都需要加X鎖,加鎖的數量,要遠遠大於滿足條件的記錄數量。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章