自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

圖文實(shí)例解析,InnoDB 存儲(chǔ)引擎中行鎖的三種算法

存儲(chǔ) 存儲(chǔ)軟件 算法
InnoDB 采用的是兩階段鎖定協(xié)議(two-phase locking protocol):即在事務(wù)執(zhí)行過程中,隨時(shí)都可以執(zhí)行加鎖操作,但是只有在事務(wù)執(zhí)行 COMMIT 或者 ROLLBACK 的時(shí)候才會(huì)釋放鎖,并且所有的鎖是在同一時(shí)刻被釋放。

[[415025]]

本文轉(zhuǎn)載自微信公眾號(hào)「飛天小牛肉」,作者飛天小牛肉。轉(zhuǎn)載本文請(qǐng)聯(lián)系飛天小牛肉公眾號(hào)。

前文提到,對(duì)于 InnoDB 來說,隨時(shí)都可以加鎖(關(guān)于加鎖的 SQL 語句這里就不說了,忘記的小伙伴可以翻一下上篇文章),但是并非隨時(shí)都可以解鎖。具體來說,InnoDB 采用的是兩階段鎖定協(xié)議(two-phase locking protocol):即在事務(wù)執(zhí)行過程中,隨時(shí)都可以執(zhí)行加鎖操作,但是只有在事務(wù)執(zhí)行 COMMIT 或者 ROLLBACK 的時(shí)候才會(huì)釋放鎖,并且所有的鎖是在同一時(shí)刻被釋放。

并且,行級(jí)鎖只在存儲(chǔ)引擎層實(shí)現(xiàn),而對(duì)于 InnoDB 存儲(chǔ)引擎來說,行級(jí)鎖又分三種,或者說有三種行級(jí)鎖算法:

  • Record Lock:記錄鎖
  • Gap Lock:間隙鎖
  • Next-Key Lock:臨鍵鎖

下面,我們來詳細(xì)解釋下這三種行鎖算法。

Record Lock 記錄鎖

顧名思義,記錄鎖就是為某行記錄加鎖,事實(shí)上,它封鎖的是該行的索引記錄。如果表在建立的時(shí)候沒有設(shè)置任何一個(gè)索引,那么這時(shí) InnoDB 存儲(chǔ)引擎會(huì)使用 “隱式的主鍵” 來進(jìn)行鎖定。

所謂隱式的主鍵就是指:如果在建表的時(shí)候沒有指定主鍵,InnoDB 存儲(chǔ)引擎會(huì)將第一列非空的列作為主鍵;如果沒有的話會(huì)自動(dòng)生成一列為 6 字節(jié)的主鍵。

那么,既然 Record Lock 是基于索引的,那如果我們的 SQL 語句中的條件導(dǎo)致索引失效(比如使用 or) 或者說條件根本就不涉及索引或者主鍵,行級(jí)鎖就將退化為表鎖。

Record Lock 示例

先來舉個(gè)對(duì)索引字段進(jìn)行查詢的例子,有數(shù)據(jù)庫如下,id 是主鍵索引:

  1. CREATE TABLE `test` ( 
  2.   `id` int(11) NOT NULL AUTO_INCREMENT, 
  3.   `username` varchar(255) DEFAULT NULL
  4.   PRIMARY KEY (`id`) 
  5. ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8; 

初始數(shù)據(jù)是這樣的:

新建兩個(gè)事務(wù),先執(zhí)行事務(wù) T1 的前兩行,也就是不要執(zhí)行 commit:

由于沒有執(zhí)行 commit,所以這個(gè)時(shí)候事務(wù) T1 沒有釋放鎖,并且鎖住了 id = 1 的記錄行,此時(shí)再來執(zhí)行事務(wù) 2 申請(qǐng) id = 2 的記錄行:

可以看見,由于鎖住的是不同的記錄行,所以兩個(gè)記錄鎖并沒有相互排斥,來看一下現(xiàn)在表中的數(shù)據(jù),由于事務(wù) 1 還沒有 commit,所以應(yīng)該是只有 id = 2 的 username 被修改了:

nice,果然。再執(zhí)行下事務(wù) 1 的 commit,id = 1 的 username 也就被修改過來啦。

行鎖退化為表鎖示例

再來看下沒有使用索引的例子:

同樣的,新建兩個(gè)事務(wù),先執(zhí)行事務(wù) T1 的前兩行,也就是不要執(zhí)行 commit。我們?cè)噲D使用 select ... for update 給 username = "user_three" 的記錄行加上記錄鎖,但是由于 username 并非主鍵也并非索引,所以實(shí)際上這里事務(wù) T1 鎖住的是整張表:

由于沒有執(zhí)行 commit,所以這個(gè)時(shí)候事務(wù) T1 沒有釋放鎖,并且鎖住了整張表。此時(shí)再來執(zhí)行事務(wù) 2 試圖申請(qǐng) id = 5 的記錄鎖,你會(huì)發(fā)現(xiàn)事務(wù) T2 會(huì)卡住,最后超時(shí)關(guān)閉事務(wù):

兩條不同記錄擁有相同的索引,會(huì)發(fā)生鎖沖突嗎?

這個(gè)問題的答案應(yīng)該很簡單吧,上面我們強(qiáng)調(diào)過,行鎖鎖住的是索引,而不是一條記錄(只不過我們平常這么說鎖住了哪條記錄,比較好理解罷了)。所以如果兩個(gè)事務(wù)分別操作的兩條不同記錄擁有相同的索引,某個(gè)事務(wù)會(huì)因?yàn)樾墟i被另一個(gè)事務(wù)占用而發(fā)生等待。

Gap Lock 間隙鎖

這里我先簡單提一嘴,下文會(huì)詳細(xì)解釋:不同于 Record Lock 是基于唯一索引的,Gap Lock 和 Next-Key Lock 都是基于非唯一索引的。

并且,不同于 Record Lock 鎖定的是某一個(gè)索引記錄,Gap Lock 和 Next-Key Lock 鎖定的都是一段范圍內(nèi)的索引記錄:

  1. select * from test where id between 1 and 10 for update

對(duì)于上述 SQL 語句,所有在(1,10)區(qū)間內(nèi)(左開右開)的記錄行都會(huì)被 Gap Lock 鎖住,所有 id 為 2、3、4、5、6、7、8、9 的數(shù)據(jù)行的插入會(huì)被阻塞,但是 1 和 10 兩條被操作的索引記錄并不會(huì)被鎖住。

注意!這里指的是鎖住所有的(1,10)區(qū)間內(nèi)的 id,也就是說即使某個(gè) id 目前并不在我們的表中比如 id = 6 ,如果你想插入一條 id = 6 的新紀(jì)錄,那對(duì)不起,不行。

Next-Key Lock 臨鍵鎖

Next-Key Lock 是結(jié)合了 Gap Lock 和 Record Lock 的一種鎖定算法,其主要目的是為了解決幻讀問題。

例如一個(gè)索引有 10,11,13 和 20 這四個(gè)值,分別對(duì)這個(gè) 4 個(gè)索引進(jìn)行加鎖操作,那么這四個(gè)操作分別對(duì)應(yīng)的 Next-Key Lock 鎖住的區(qū)間是:

  • (-∞, 10]
  • (10, 11]
  • (11, 13]
  • (13, 20]
  • (20, +∞]

細(xì)心的同學(xué)應(yīng)該已經(jīng)注意到了,和 Gap Lock 的不同之處就在于,Next-Key Lock 鎖定的區(qū)間是左開右閉的,也就是說它是包含當(dāng)前被操作的索引記錄的。

在 InnoDB 默認(rèn)的隔離級(jí)別 REPEATABLE-READ 下,行鎖默認(rèn)使用的算法就是 Next-Key Lock。但是,如果操作的索引是唯一索引或主鍵,InnoDB 會(huì)對(duì) Next-Key Lock 進(jìn)行優(yōu)化,將其降級(jí)為 Record Lock,即僅鎖住索引本身,而不是范圍。

由于主鍵也是一種唯一索引,所以我們可以這么說:Record Lock 是基于唯一索引的,而 Next-Key Lock 是基于非唯一索引的。

需要注意的,當(dāng)操作的索引為非唯一索引時(shí),InnoDB 會(huì)先用 Record Lock 鎖住對(duì)應(yīng)的唯一索引,再用 Next-Key Lock 和 Gap Lock 對(duì)這個(gè)非唯一索引進(jìn)行處理,而不僅僅是鎖住這個(gè)非唯一索引。具體地我們舉個(gè)例子來看下。

Next-Key Lock 示例

假設(shè)我們?yōu)樯厦?test 表中新增一個(gè)字段,并設(shè)置為非唯一索引:

  1. CREATE TABLE `test` ( 
  2.   `id` int(11) NOT NULL AUTO_INCREMENT, 
  3.   `username` varchar(255) DEFAULT NULL
  4.   `class` int(11) NOT NULL
  5.   PRIMARY KEY (`id`), 
  6.   KEY `index_class` (`class`) USING BTREE COMMENT '非唯一索引' 
  7. ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8; 

插入一些數(shù)據(jù):

開啟一個(gè)事務(wù) 1 執(zhí)行如下的操作語句:

  1. select * from test where class = 3 for update

在這種情況下,InnoDB 事實(shí)上會(huì)加上三種行鎖(select * ... from update 加的是行級(jí)寫鎖即 X 鎖):

1)給主鍵索引 id = 105 加上 Record Lock

2)對(duì)于非唯一索引 class = 3,其加上的是 Next-Key Lock,鎖定的范圍是 (1,3]

3)另外,特別需要注意的是,InnoDB 存儲(chǔ)引擎還會(huì)對(duì)非唯一索引 class 的下一個(gè)鍵值加上 Gap Lock(表中 class = 3 的下個(gè)鍵值是 6),所以還有一個(gè) class 索引范圍為 (3,6) 的間隙鎖

總結(jié)下 2)和 3),對(duì)于這條 SQL 語句,InnoDB 存儲(chǔ)引擎鎖定地 class 索引范圍是 (1, 6)

下面我們用實(shí)踐來驗(yàn)證理論,再開啟一個(gè)事務(wù) 2,執(zhí)行下述的語句:

不出所料,由于在事務(wù) 1 中執(zhí)行的 SQL 語句已經(jīng)對(duì)主鍵索引中列 a=105 的記錄加上了 X 鎖,所以此處再去獲取 這個(gè)記錄的 X 鎖會(huì)被阻塞住。

再用一個(gè)事務(wù)來執(zhí)行下述 SQL 語句:

主鍵插入 104 沒有任何問題,但是插入的 class 索引值 2 在被鎖定的范圍 (1,6) 中,因此執(zhí)行同樣會(huì)被阻塞住。

經(jīng)過上面的分析,大家一定能夠知道下面的 SQL 語句是可以正常執(zhí)行的:

Attention

 

需要注意的是,Next-Key Lock 降級(jí)為 Record Lock 僅存在于操作所有的唯一索引列的情況。若唯一索引由多個(gè)列組成,而操作的僅是多個(gè)唯一索引列中的其中一個(gè),那么 InnoDB 存儲(chǔ)引擎依然使用 Next-Key Lock 進(jìn)行鎖定。

 

責(zé)任編輯:武曉燕 來源: 飛天小牛肉
相關(guān)推薦

2017-12-29 08:26:28

存儲(chǔ)引擎MySQL

2010-04-16 15:12:12

ORACLE鎖機(jī)制

2021-07-19 12:51:34

存儲(chǔ)InnoDB行鎖

2010-08-31 13:18:22

CSS浮動(dòng)

2013-04-01 09:55:03

OpenStack存儲(chǔ)

2022-11-30 15:15:48

2021-08-10 10:14:14

存儲(chǔ)接口存儲(chǔ)設(shè)備存儲(chǔ)

2023-09-13 09:52:14

分布式鎖Java

2010-09-25 15:15:32

2009-07-20 15:08:41

Spring實(shí)例化Be

2010-09-26 17:09:22

日內(nèi)數(shù)據(jù)保護(hù)

2010-09-13 12:19:03

2022-07-13 16:06:16

Python參數(shù)代碼

2013-05-07 09:39:14

軟件定義網(wǎng)絡(luò)SDNOpenFlow

2018-07-04 09:19:37

存儲(chǔ)類型對(duì)象存儲(chǔ)

2019-06-11 16:11:16

MySQLMyISAMInnoDB

2019-06-04 15:27:49

InnoDB存儲(chǔ)引擎

2024-06-12 14:03:31

MySQLInnoDB

2009-08-26 18:14:11

C#排序算法

2011-11-15 09:42:35

云存儲(chǔ)云計(jì)算
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)