tft每日頭條

 > 職場

 > 面試官問在mysql中插入一百萬數據

面試官問在mysql中插入一百萬數據

職場 更新时间:2024-07-24 05:20:24

今日分享開始啦,請大家多多指教~

面試官問在mysql中插入一百萬數據(面試命中率90面試官)1

鎖分類

當多個事務或進程訪問同一個資源時,為了保證數據的一緻性就會用到鎖機制,在MySQL中鎖有多種不同的分類。

以操作粒度區分

行級鎖、表級鎖和頁級鎖

  • 表級鎖:每次操作鎖住整張表。鎖定的粒度大、開銷小、加鎖快;不會發生死鎖,但發生鎖沖突的概率極高,并發度最低,應用在InnoDB、MyISAM、BDB中;
  • 行級鎖:每次操作鎖住一行數據。鎖定的粒度小、開銷大、加鎖慢;會出現死鎖,發生鎖沖突的概率極低,并發度最高,應用在InnoDB中;
  • 頁級鎖:每次鎖定相鄰的一組記錄。鎖定粒度、開銷、加鎖時間介于行級鎖和表級鎖之間;會出現死鎖,并發度一般,應用在BDB中;

面試官問在mysql中插入一百萬數據(面試命中率90面試官)2

以操作類型區分

讀鎖、寫鎖

  • 讀鎖(S):共享鎖,針對同一份數據,多個讀操作可以同時進行不會互相影響;
  • 寫鎖(X):排它鎖,當前寫操作沒有完成時,會阻塞其他讀和寫操作;

為了允許行鎖和表鎖的共存,實現多粒度的鎖機制,InnoDB還有兩種内部使用的意向鎖,這兩種意向鎖都是表鎖:

  • 意向讀鎖(IS)、意向寫鎖(IX):屬于表級鎖,S和X主要針對行級鎖。在對表記錄添加S或X鎖之前,會先對表添加IS和IX鎖,表明某個事務正在持有某些行的鎖、或該事務準備去持有鎖;意向鎖存在是為了協調鎖之間的關系,支持多粒度鎖共存;

為什麼意向鎖是表級鎖?

為了減少确認次數,提升性能:如果意向鎖是行鎖,需要遍曆每一行去确認數據是否已經加鎖;如果是表鎖的話,隻需要判斷一次就知道有沒有數據行被鎖定;

意向鎖是如何支持行級鎖、表級鎖共存的?

舉例

  • S鎖:事務A對記錄添加了S鎖,可以對記錄進行讀取操作,不能做修改,其它事務可以對該記錄追加S鎖,但是不能追加X鎖,追加X鎖需要等記錄的S鎖全部釋放;
  • X鎖:事務A對記錄添加了X鎖,可以對記錄進行讀和修改操作,其它事務不能對該記錄做讀和修改操作。
意向鎖、共享鎖和排它鎖之間的兼容關系

面試官問在mysql中插入一百萬數據(面試命中率90面試官)3

  • 意向鎖相互兼容,因為IX和IS隻是表明申請更低層次的級别元素的X、S操作;
  • 表級S和X、IX不兼容,因為上了表級S鎖後,不允許其它事務再加X鎖;
  • 上了表級X鎖後,會修改數據,所以表級X鎖和 IS、IX、S、X(即使是行排他鎖,因為表級鎖定的行肯定包括行級鎖定的行,所以表級X和IX、行級X)不兼容。

以操作性能區分

樂觀鎖、悲觀鎖

  • 樂觀鎖:一般采用的方式是對數據記錄版本進行對比,在數據更新提交時才會進行沖突檢測,如果發現沖突了,則提示錯誤信息;
  • 悲觀鎖:在對一條記錄進行修改時,為了避免被其他人修改,在修改數據之前先鎖定再修改的方式。共享鎖和排它鎖是悲觀鎖的不同實現。
InnoDB的行鎖

行鎖的實現原理

意向鎖是InnoDB自動加的,不需要用戶幹預;對于 UPDATE 、DELETE 和 INSERT 語句,InnoDB會自動給涉及的數據集增加排他鎖(X);對于普通的 SELECT 語句,InnoDB不會加任何鎖;事務也可以通過以下語句顯式的給記錄集加共享鎖

SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE 和排它鎖 SELECT * FROM table_name WHERE ... FOR UPDATE 。

在InnoDB中,支持行鎖和表鎖,行鎖又分為共享鎖和排它鎖。InnoDB行鎖是通過對索引數據頁上的記錄加鎖實現的。由于InnoDB行鎖的實現特點,導緻隻有通過索引條件檢索并且執行計劃中真正使用到索引時InnoDB才會使用行鎖 ;并且不論使用主鍵索引、唯一索引、普通索引,InnoDB都會使用行鎖來進行加鎖,否則InnoDB将使用表鎖。

由于InnoDB是針對索引加鎖,而不是針對記錄加鎖,所以即使多個事務訪問不同行的記錄,但如果使用的是相同的索引,還是會出現鎖沖突的情況,甚至出現死鎖。

行鎖的不同實現

行鎖的主要實現有三種: Record Lock 、 Gap Lock 和 Next-Key Lock 。

RecordLock:記錄鎖,鎖定單個行記錄的鎖,RC和RR隔離級别支持。

GapLock:間隙鎖,鎖定索引記錄間隙,确保索引記錄的間隙不變。範圍鎖,RR隔離級别支持。(加鎖之後間隙範圍内不允許插入數據,防止發生幻讀)

  • Insert Intention:插入意向鎖,插入意向鎖中雖然含有意向鎖三個字,但是它不屬于意向鎖,而是屬于間隙鎖,在insert時産生;意向鎖是表鎖,而插入意向鎖是行鎖。

Next-Key Lock:臨鍵鎖,它是記錄鎖和間隙鎖的結合體,鎖住數據的同時鎖住數據前後範圍。記錄鎖 範圍鎖,RR隔離級别支持。

insert 的加鎖流程:

執行 insert 之後,如果沒有任何沖突,在 show engine innodb status 命令中是看不到任何鎖的,這是因為 insert 加的是隐式鎖。什麼是隐式鎖?隐式鎖的意思就是沒有鎖!

所以,根本就不存在先加插入意向鎖,再加排他記錄鎖的說法,在執行 insert 語句時,什麼鎖都不會加。當其他事務執行 select ... lock in share mode 時觸發了隐式鎖的轉換。

InnoDb 在插入記錄時,是不加鎖的。如果事務 A 插入記錄且未提交,這時事務 B 嘗試對這條記錄加鎖:事務 B 會先去判斷記錄上保存的事務 id 是否活躍,如果活躍的話,那麼就幫助事務 A 去建立一個鎖對象(排他記錄鎖),然後自身進入等待事務 A 狀态,這就是所謂的隐式鎖轉換為顯式鎖。

結論:

執行 insert 語句,判斷是否有和插入意向鎖沖突的鎖,如果有,加插入意向鎖,進入鎖等待;如果沒有,直接寫數據,不加任何鎖;

執行 select ... lock in share mode 語句,判斷記錄上是否存在活躍的事務,如果存在,則為 insert 事務創建一個排他記錄鎖,并将自己加入到鎖等待隊列;

MySQL使用間隙鎖的目的

間隙鎖的主要目的是為了防止幻讀,其主要通過兩個方面實現這個目的:

  1. 防止間隙内有新數據被插入
  2. 防止已存在的數據,更新成間隙内的數據

另外一方面,是為了滿足其恢複和複制的需要。對于基于語句的日志格式的恢複和複制而言,由于MySQL的BINLONG是按照事務提交的先後順序記錄的,因此要正确恢複或者複制數據,就必須滿足:在一個事務未提交前,其他并發事務不能插入滿足其鎖定條件的任何記錄,根本原因還是不允許出現幻讀。

鎖規則

  1. 規則1:加鎖的基本單位是臨鍵鎖(Next-key Lock)
  2. 規則2:查找過程中訪問的對象才會加鎖
  3. 優化1:索引上的等值查詢,給唯一鍵加索引的時候,如果查詢值存在,臨鍵鎖(Next-key Lock)會退化成記錄鎖(Record Lock);如果查詢值不存在,會按照優化2進行優化
  4. 優化2:索引上的等值查詢,向右遍曆時且最近一個值不滿足等值條件時,臨鍵鎖(Next-key Lock)會退化成間隙鎖(Gap Lock)
  5. bug1:唯一索引上的範圍查詢會訪問到不滿足條件的第一個值為止。

在mySQL8.0.18及以上已經沒有這個bug

鎖結構

對不同記錄加鎖時,如果符合下邊這些條件:

  • 在同一個事務中進行加鎖操作
  • 被加鎖的記錄在同一個頁面中
  • 加鎖的類型是一樣的
  • 等待狀态是一樣的

那麼這些記錄的鎖就可以被放到一個鎖結構中。

鎖的兼容性

面試官問在mysql中插入一百萬數據(面試命中率90面試官)4

從圖中可以看出,橫向為事務A擁有的鎖,豎向為事務B想要獲取的鎖;舉例: 如果前一個事務A 持有 gap 鎖 或者 next-key 鎖的時候,後一個事務B如果想要持有 Insert Intention 鎖的時候會不兼容,出現鎖等待。

加鎖
  1. SELECT ... FROM ... :InnoDB采用MVCC機制實現非阻塞讀,對于普通的 SELECT 語句,InnoDB不加鎖。
  2. SELECT ... FROM ... LOCK In SHARE MODE :顯式追加共享鎖,InnoDB會使用臨鍵鎖(Next-key Lock)進行處理,如果發現了唯一索引,可以降級為記錄鎖(RecordLock)。
  3. SELECT ... FROM ... FOR UPDATE :顯式追加排它鎖,InnoDB會使用Next-Key Lock鎖進行處理,如果發現唯一索引,可以降級為RecordLock鎖。
  4. UPDATE ... WHERE :InnoDB會使用臨鍵鎖(Next-key Lock)進行處理,如果掃描發現唯一索引,可以降級為記錄鎖(RecordLock)。
  5. DELETE ... WHERE:InnoDB會使用臨鍵鎖(Next-key Lock)進行處理,如果掃描發現唯一索引,可以降級為記錄鎖(RecordLock)。
  6. insert:InnoDB會在将要插入的那一行設置一個排他的記錄鎖(RecordLock)。

以 update t1 set name=‘XX’ where id=10 操作為例:

主鍵加鎖

面試官問在mysql中插入一百萬數據(面試命中率90面試官)5

加鎖行為:僅在id=10的主鍵索引記錄上加X鎖。

唯一鍵加鎖

面試官問在mysql中插入一百萬數據(面試命中率90面試官)6

加鎖行為:先在唯一索引id上加X鎖,然後在id=10的主鍵索引記錄上加X鎖。

非唯一鍵加鎖

面試官問在mysql中插入一百萬數據(面試命中率90面試官)7

加鎖行為:對滿足id=10條件的記錄和主鍵分别加X鎖,然後在(6,c)-(10,b)、(10,b)-(10,d)、(10,d)(11,f)範圍分别加Gap Lock。

無索引加鎖

面試官問在mysql中插入一百萬數據(面試命中率90面試官)8

加鎖行為:表裡所有行和間隙都會加X鎖。(當沒有索引時,會導緻全表鎖定,因為InnoDB引擎 鎖機制是基于索引實現的記錄鎖定)。

鎖模拟

查看事務、鎖的語句:

面試官問在mysql中插入一百萬數據(面試命中率90面試官)9

輸出結果解析:

面試官問在mysql中插入一百萬數據(面試命中率90面試官)10

面試官問在mysql中插入一百萬數據(面試命中率90面試官)11

數據準備:

面試官問在mysql中插入一百萬數據(面試命中率90面試官)12

鎖舉例

鎖等待超時:ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

死鎖:1213 Deadlock found when trying to get lock

等值查詢間隙鎖

面試官問在mysql中插入一百萬數據(面試命中率90面試官)13

分析:

  1. 由于表T沒有 id=7 這條記錄,加鎖單位是 Next-key Lock ,事務1加鎖範圍是 (5, 10] ,因為 id=7 是一個等值查詢,根據優化規則, id=10 不滿足條件, Next-key Lock 退化成 Gap Lock ,因此最終加鎖範圍是 (5, 10) 。
  2. Session2 想要向這個間隙中插入 id=8 的記錄必須等待 Session1 事務提交後才可以。
  3. Session3 想要插入 id=11 ,不在加鎖範圍,所以可以插入成功。

這是如果有 Session4 想要更新 id=8 的記錄,是可以執行成功的,因為間隙鎖之間互不沖突;

非唯一鍵等值鎖

面試官問在mysql中插入一百萬數據(面試命中率90面試官)14

分析:

  1. Session1 給索引 c 上的 c=5 這一列加上讀鎖,根據規則1,加鎖單位為 Next-key Lock ,因此會給 (0, 5] 區間加上 Next-key Lock
  2. 因為c是普通索引,所以訪問 c=5 之後還要向右遍曆,直到 c=10 停止,根據規則2訪問到的都要加鎖,所以加鎖範圍為 (5, 10] ,根據優化2,等值查詢退化為 Gap Lock ,變為 (5, 10),所以最終的加鎖範圍是 (0, 10);
  3. Session2 想要插入 id=7 的記錄,要等待 Session1 提交之後才可以成功插入,因為 Session1 的間隙範圍是(5, 10);
  4. 根據原則2,訪問到的對象才會加鎖,這個查詢使用覆蓋索引,并不需要訪問主鍵索引,所以主鍵索引上沒有加任何鎖。所以 Session3 的語句可以正常執行;

LOCK IN SHARE MODE; 隻鎖覆蓋索引,FOR UPDATE; 會順便鎖上主鍵索引;

主鍵索引範圍鎖

select * from t where id=10 for update;

select * from t where id>=10 and id<11 for update;

對于以上兩條SQL,加鎖的範圍不一緻,第一條是id=10 的行鎖,第二條是 (10, 15] 的 Next-key Lock。

面試官問在mysql中插入一百萬數據(面試命中率90面試官)15

分析:

  1. Session1 根據規則1,加鎖單位為 Next-key Lock ,因為 id>=10 是範圍查詢,直到找到 id=15 停止,最終 Session1 的加鎖範圍是 (10, 15]
  2. Session3 當去 update 一個存在的值是,給該行添加 Record Lock ,由于 Record Lock 和 Next-key Lock 不兼容,所以阻塞

如果 Session3 更新一個 (10, 15) 的值,則會阻塞;

非唯一索引範圍鎖

面試官問在mysql中插入一百萬數據(面試命中率90面試官)16

分析:

Session1 給索引c加上了 (5,10], (10,15] 兩個 Next-key Lock ;由于是範圍查詢,不觸發優化,不會退化成間隙鎖

非唯一索引等值鎖for Update

數據準備:

面試官問在mysql中插入一百萬數據(面試命中率90面試官)17

在表t中,a列有普通索引,所以可能鎖定的範圍有:

(-∞, 1], (1, 3], (3, 5], (5, 8], (8, 11], (11, ∞)

面試官問在mysql中插入一百萬數據(面試命中率90面試官)18

Session1 執行完成之後預期加鎖範圍為 (5, 8] 和 (8, 11],由于鎖優化策略,退化成間隙鎖,範圍變成 (5, 8] 和 (8, 11) ,也就是 (5, 11) ,插入12和4不會阻塞很好理解。但是 5不在鎖的範圍内,還是被鎖上了。

是因為如果索引值相同的話,會根據id進行排序加鎖,所以最終的加鎖範圍是索引a的 (5, 4) 到 (11, 6) 的範圍。

面試官問在mysql中插入一百萬數據(面試命中率90面試官)19

死鎖模拟

死鎖模拟-場景1

AB BA操作問題

數據準備:

面試官問在mysql中插入一百萬數據(面試命中率90面試官)20

面試官問在mysql中插入一百萬數據(面試命中率90面試官)21

死鎖模拟-場景2

S-lock 升級 X-lock

數據準備:

沿用簡單場景1數據

面試官問在mysql中插入一百萬數據(面試命中率90面試官)22

分析:

  1. Session1 獲取到 S-Lock
  2. Session2 嘗試獲取到 X-Lock ,但是被 Session1 的S-Lock 阻塞
  3. Session1 想要獲取到 X-Lock,本身擁有一個 S-Lock ,但是Session2 申請 X-Lock 在前,需要等待 Session2 釋放之後才能提升到 X-Lock,兩個事務造成資源争搶導緻死鎖

死鎖模拟-場景3

數據準備:

面試官問在mysql中插入一百萬數據(面試命中率90面試官)23

面試官問在mysql中插入一百萬數據(面試命中率90面試官)24

分析:

事務一在插入時由于跟事務二插入的記錄唯一鍵沖突,所以對 a=10 這個唯一索引加 S 鎖(Next-key)并處于鎖等待,事務二再插入 a=9 這條記錄,需要獲取插入意向鎖(lock_mode X locks gap before rec insert intention)和事務一持有的 Next-key 鎖沖突,從而導緻死鎖。

死鎖模拟-場景4

面試官問在mysql中插入一百萬數據(面試命中率90面試官)25

死鎖日志:

面試官問在mysql中插入一百萬數據(面試命中率90面試官)26

面試官問在mysql中插入一百萬數據(面試命中率90面試官)27

  1. UPDATE 的 WHERE 子句沒有滿足條件的記錄,而對于不存在的記錄并且在RR級别下,UPDATE 加鎖類型為間隙鎖(Gap Lock),間隙鎖(Gap Lock)之間是兼容的,所以兩個事務都能成功執行 UPDATE;這裡的gap範圍是索引id列 (5, 10) 的範圍。
  2. INSERT 時,其加鎖過程為先在插入間隙上獲取插入意向鎖,插入數據後再獲取插入行上的排它鎖。又插入意向鎖與間隙鎖(Gap Lock)和臨鍵鎖(Next-key Lock)沖突,即一個事務想要獲取插入意向鎖,如果有其他事務已經加了(Gap Lock)或臨鍵鎖(Next-key Lock),則會阻塞。
  3. 場景中兩個事務都持有間隙鎖(Gap Lock),然後又申請插入意向鎖,此時都被阻塞,循環等待造成死鎖。
  • 記錄鎖(LOCK_REC_NOT_GAP): lock_mode X locks rec but not gap
  • 間隙鎖(LOCK_GAP): lock_mode X locks gap before rec
  • Next-key 鎖(LOCK_ORNIDARY): lock_mode X
  • 插入意向鎖(LOCK_INSERT_INTENTION): lock_mode X locks gap before rec insert intention

并不是在日志裡看到 lock_mode X 就認為這是 Next-key 鎖,因為還有一個例外:如果在 supremum record 上加鎖,locks gap before rec 會省略掉,間隙鎖會顯示成 lock_mode X,插入意向鎖就會顯示成 lock_mode X insert intention。

INSERT 語句,會嘗試獲取lock mode S waiting 鎖,這是為了檢測唯一鍵是否重複,必須進行一次當前讀,要加 S 鎖。

INSERT 加鎖分幾個階段:先檢查唯一鍵約束,加 S 鎖,再加插入意向鎖,最後插入成功時升級為 X 鎖。

面試官問在mysql中插入一百萬數據(面試命中率90面試官)28

今日份分享已結束,請大家多多包涵和指點!

面試官問在mysql中插入一百萬數據(面試命中率90面試官)29

如何獲取?

轉發分享此文,後台私信小編:“1”即可獲取。(注:轉發分享,感謝大家)

,

更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!

查看全部

相关職場资讯推荐

热门職場资讯推荐

网友关注

Copyright 2023-2024 - www.tftnews.com All Rights Reserved