欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

MySQL之鎖類型解讀

 更新時(shí)間:2025年02月07日 14:14:00   作者:空說(shuō)  
MySQL鎖類型包括讀鎖(共享鎖)和寫鎖(排他鎖),并介紹了意向鎖、自增鎖、元數(shù)據(jù)鎖、行級(jí)鎖和間隙鎖等概念,悲觀鎖和樂(lè)觀鎖是兩種不同的鎖設(shè)計(jì)思想,悲觀鎖在每次操作前加鎖,適用于并發(fā)沖突多的場(chǎng)景;樂(lè)觀鎖在更新時(shí)判斷數(shù)據(jù)是否被修改

MySQL鎖類型解讀

鎖的分類圖,如下:

鎖操作類型劃分

  • 讀鎖 : 也稱為共享鎖 、英文用S表示。針對(duì)同一份數(shù)據(jù),多個(gè)事務(wù)的讀操作可以同時(shí)進(jìn)行而不會(huì)互相影響,相互不阻塞的。
  • 寫鎖 : 也稱為排他鎖 、英文用X表示。當(dāng)前寫操作沒(méi)有完成前,它會(huì)阻斷其他寫鎖和讀鎖。這樣就能確保在給定的時(shí)間里,只有一個(gè)事務(wù)能執(zhí)行寫入,并防止其他用戶讀取正在寫入的同一資源。
  • 對(duì)于InnoDB引擎,讀鎖和寫鎖可以加在表上,或者行上

鎖定讀

對(duì)讀取的記錄加S鎖:

select...lock in share mode;
#或
select...for share;#(8.0新增語(yǔ)法)。。

若當(dāng)前事務(wù)執(zhí)行了該語(yǔ)句,則會(huì)給該記錄加S鎖,并允許別的事務(wù)繼續(xù)獲取該記錄的S鎖,比如別的事務(wù)也使用 SELECT... LOCK IN SHAREMODE 語(yǔ)句來(lái)讀取這些記錄

但是不能獲取這些記錄的X鎖,比如別的事務(wù)不能直接修改這些記錄,會(huì)阻塞直到當(dāng)前事務(wù)提交之后將這些記錄上的S鎖釋放掉。

對(duì)讀取的記錄加X鎖:

select...for update;

如果當(dāng)前事務(wù)執(zhí)行了該語(yǔ)句,則會(huì)給該記錄加X鎖,即不允許別的事務(wù)獲取這些事務(wù)S鎖,也不允許獲取X鎖...

MySQL8新特性

在8.0版本中,SELECT ... FOR UPDATE, SELECT .. FOR SHARE 添加NOWAIT、 SKIP LOCKED 語(yǔ)法,跳過(guò)鎖等待,或者跳過(guò)鎖定。

通過(guò)添加NOWAIT、SKIP LOCKED語(yǔ)法,能夠立即返回。如果查詢的行已經(jīng)加鎖:

  • 那么NOWAIT會(huì)立即報(bào)錯(cuò)返回
  • 而SKIP LOCKED也會(huì)立即返回,只是返回的結(jié)果中不包含被鎖定的行

寫操作

寫操作無(wú)非是delete,update,insert

  • DELETE

底層是先在 B+ 樹(shù)中定位到這條記錄的位置,然后獲取這條記錄的X鎖,再執(zhí)行 delete操作。

  • UPDATE

情況1: 未修改該記錄的鍵值,并且被更新的列占用的存儲(chǔ)空間在修改前后未發(fā)生變化。

則在 B+ 樹(shù)中定位到這條記錄的位置,然后獲取記錄的 X鎖,最后在原記錄的位置進(jìn)行修改操作。

情況2: 修改了該記錄的鍵值,則相當(dāng)于在原記錄上做 DELETE 操作之后再來(lái)一次INSERT操作,加鎖操作。需要按照 DELETE 和 INSERT 的規(guī)則進(jìn)行了。

2.意向鎖

InnoDB 支持多粒度鎖,它允許行級(jí)鎖表級(jí)鎖共存,而意向鎖就是其中的一種表鎖

  • 意向鎖的存在是為了協(xié)調(diào)行鎖和表鎖的關(guān)系
  • 意向鎖是一種不與行級(jí)鎖沖突表級(jí)鎖 ,這一點(diǎn)非常重要
  • 意向鎖是自動(dòng)創(chuàng)建的,自動(dòng)聲明其上級(jí)獲取過(guò)鎖這一動(dòng)作,輪到時(shí)就會(huì)有排隊(duì)權(quán)

意向鎖分為兩種:

意向共享鎖: 事務(wù)有意向?qū)Ρ碇械哪承┬屑?code>共享鎖(S鎖)

#事務(wù)要獲取某些行的S鎖,必須先獲得表的IS 鎖。
select column from table ... lock in share mode;

意向排他鎖: 事務(wù)有意向?qū)Ρ碇械哪承┬屑?code>排他鎖(X鎖)

#事務(wù)要獲取某些行的X鎖,必須先獲得表的 IX 鎖
select column from table ... for update;

意向鎖是由存儲(chǔ)引擎自己維護(hù)的 ,用戶無(wú)法手動(dòng)操作意向鎖,在為數(shù)據(jù)行加共享/排他鎖之前,InooDB 會(huì)先獲取該數(shù)據(jù)行所在數(shù)據(jù)表的對(duì)應(yīng)意向鎖

意向鎖作用

現(xiàn)在有兩個(gè)事務(wù)T1和T2,其中T2試圖在該表級(jí)別上應(yīng)用共享或排它鎖

  • 如果沒(méi)有意向鎖存在,那么T2就需要去檢查各個(gè)頁(yè)或行是否存在鎖
  • 如果存在意向鎖,那么此時(shí)就會(huì)受到由T1控制的表級(jí)別意向鎖的阻塞,T2在鎖定該表前不必檢查各個(gè)頁(yè)或行鎖,而只需檢查表上的意向鎖。其實(shí)就是在更大一級(jí)別的空間示意里面是否已經(jīng)上過(guò)鎖!
  • 在數(shù)據(jù)表的場(chǎng)景中,如果我們給某一行數(shù)據(jù)加上了排它鎖,數(shù)據(jù)庫(kù)會(huì)自動(dòng)給更大一級(jí)的空間,比如數(shù)據(jù)頁(yè)或數(shù)據(jù)表加上意向鎖,告訴其他人這個(gè)數(shù)據(jù)頁(yè)或數(shù)據(jù)表已經(jīng)有人上過(guò)排它鎖了,這樣當(dāng)其他人想要獲取數(shù)據(jù)表排它鎖的時(shí)候,只需要看是否有人已獲取這個(gè)數(shù)據(jù)表的意向排他鎖即可

假設(shè)事務(wù)A獲取了某一行的排他鎖,并未提交

begin;
select * from teacher where id=6 for update;

事務(wù)B想要獲取teacher表的讀鎖,語(yǔ)句如下

begin;
lock tables teacher read;

因?yàn)楣蚕礞i與排他鎖互斥,所以事務(wù)B在試圖對(duì) teacher 表加共享鎖的時(shí)候,必須保證兩個(gè)條件。

  • 當(dāng)前沒(méi)有其他事務(wù)持有 teacher 表的排他鎖
  • 當(dāng)前沒(méi)有其他事務(wù)持有 teacher 表中任意一行的排他鎖

為了檢測(cè)是否滿足第二個(gè)條件,事務(wù)B必須在確保 teacber 表不存在任何排他鎖的前提下,去檢測(cè)表中的每一行是否存在排他鎖。很明顯這是一個(gè)效率很差的做法,但是有了意向鎖之后,情況就不一樣了。

總結(jié):

意向鎖是一個(gè)虛擬的鎖,只是為了讓別的事務(wù)知道 這里有行級(jí)鎖 所以不能加某些表級(jí)鎖

3.自增鎖

在使用MySQL過(guò)程中,我們可以為表的某個(gè)列添加 AUTO_INCREMENT屬性。舉例:

CREATE TABLE `teacher` (
     `id` int NOT NULL AUTO_INCREMENT,
     `name` varchar(255) NOT NULL,
     PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

這意味著插入語(yǔ)句時(shí)不需要為其賦值,SQL語(yǔ)句修改如下所示

INSERT INTO `teacher` (name) VALUES ('zhangsan'), ('lisi');
?
#上邊的插入語(yǔ)句并沒(méi)有為id列顯式賦值,所以系統(tǒng)會(huì)自動(dòng)為它賦上遞增的值,結(jié)果如下所示
mysql> select * from teacher;
+----+----------+
| id | name   |
+----+----------+
| 1 | zhangsan |
| 2 | lisi   |
+----+----------+
2 rows in set (0.00 sec)

所有插入數(shù)據(jù)的方式總共分為三類,分別是:“ Simple inserts ”,“ Bulk inserts ”和“ Mixed-mode inserts ”。

對(duì)于上面案例,MySQL中采用了自增鎖 的方式來(lái)實(shí)現(xiàn),自增鎖是向含有AUTO_INCREMENT列的表中插入數(shù)據(jù)時(shí)的一種特殊的表級(jí)鎖,在執(zhí)行插入語(yǔ)句時(shí)加一個(gè)AUTO-INC鎖,分配遞增的值,執(zhí)行結(jié)束后,再把AUTO-INC鎖釋放掉。

一個(gè)事務(wù)在持有AUTO-INC鎖的過(guò)程中,其他事務(wù)的插入語(yǔ)句都要被阻塞,可以保證一個(gè)語(yǔ)句中分配的遞增值是連續(xù)的。也正因?yàn)榇?,其并發(fā)性不高,當(dāng)我們向一個(gè)有AUTO_INCREMENT關(guān)鍵字的主鍵插入值的時(shí)候,每條語(yǔ)句都要對(duì)這個(gè)表鎖進(jìn)行競(jìng)爭(zhēng),這樣的并發(fā)潛力其實(shí)是很低下的,所以innodb通過(guò) innodb_autoinc_lock_mode的不同取值來(lái)提供不同的鎖定機(jī)制,來(lái)顯著提高SQL語(yǔ)句的可伸縮性和性能。

#innodb_autoinc_lock_mode=0(傳統(tǒng)鎖定模式)
所有insert語(yǔ)句都會(huì)獲得自增鎖,這種鎖定是全局性的,即它會(huì)阻止其他事務(wù)同時(shí)進(jìn)行插入操作,直到當(dāng)前插入完成,即上面例子,并發(fā)性差
?
#innodb_autoinc_lock_mode=1(連續(xù)鎖定模式)
mysql8之前的默認(rèn)模式,當(dāng)執(zhí)行 INSERT 時(shí),InnoDB 會(huì)先檢查是否有可用的自增值,如果有,則立即分配該值給新行,然后才加鎖
?
#innodb_autoinc_lock_mode=2(交錯(cuò)鎖定模式)
InnoDB 會(huì)預(yù)先分配一組自增ID(數(shù)量由 innodb_autoinc_cache 控制),然后將這些ID分配給后續(xù)的插入操作,多個(gè)客戶端可以同時(shí)從預(yù)分配的ID池中獲取ID并插入新行,從而大大減少了鎖等待的時(shí)間,但由于多個(gè)語(yǔ)句會(huì)同時(shí)需要數(shù)字,所以任何給定插入的行生成的值可能不是連續(xù)的

4.元數(shù)據(jù)鎖(MDL鎖)

MDL 的作用是,保證讀寫的正確性。比如,如果一個(gè)查詢正在遍歷一個(gè)表中的數(shù)據(jù),而執(zhí)行期間另一個(gè)線程對(duì)這個(gè)表結(jié)構(gòu)做變更 ,增加了一列,那么查詢線程拿到的結(jié)果跟表結(jié)構(gòu)對(duì)不上,肯定是不行的。

因此,當(dāng)對(duì)一個(gè)表做增刪改查操作的時(shí)候,加MDL讀鎖;當(dāng)要對(duì)表做結(jié)構(gòu)變更操作的時(shí)候,加 MDL寫鎖。解決DML和DDL操作之間的一致性問(wèn)題,不需要顯式使用,在訪問(wèn)一個(gè)表時(shí)會(huì)自動(dòng)加上

行級(jí)鎖

顧名思義,就是鎖住頁(yè)中某一行記錄,注意點(diǎn)是 行級(jí)鎖只在存儲(chǔ)引擎層實(shí)現(xiàn),鎖的力度小,發(fā)生鎖沖突概率低,并發(fā)度高

缺點(diǎn)是對(duì)于鎖的開(kāi)銷比較大,加鎖會(huì)比較慢,容易出現(xiàn)死鎖情況

  • 數(shù)據(jù)準(zhǔn)備
CREATE TABLE accounts (
    id INT PRIMARY KEY,
    balance DECIMAL(10, 2)
);
?
INSERT INTO accounts (id, balance) VALUES (1, 1000), (2, 2000);
  • 記錄鎖

記錄鎖僅僅把一條記錄 鎖上,官方的類型名稱為:LOCK_REC_NOT_GAP。比如我們把id值為8的那條記錄加一個(gè)記錄鎖如圖所示,僅僅是鎖住了id值為8的記錄,對(duì)周圍的數(shù)據(jù)沒(méi)有影響。

記錄鎖是有S鎖和X鎖之分的,稱之為 S型(讀)記錄鎖X型(寫)記錄鎖

  • 當(dāng)一個(gè)事務(wù)獲取了一條記錄的讀鎖后,其他事務(wù)可以繼續(xù)獲取該記錄的讀鎖,但不可以繼續(xù)獲取寫鎖;
  • 當(dāng)一個(gè)事務(wù)獲取了一條記錄的寫鎖后,其他事務(wù)既不可以獲取該記錄的讀鎖,也不可以繼續(xù)獲取寫鎖。

死鎖

接下來(lái),我們來(lái)看一個(gè)可能引起死鎖的情況,假設(shè)事務(wù) D 和事務(wù) E 同時(shí)運(yùn)行,并且它們都試圖更新兩個(gè)賬戶的余額:

-- 啟動(dòng)事務(wù) D
START TRANSACTION;
-- 獲取賬戶1的排他鎖
SELECT * FROM accounts WHERE id = 1 FOR UPDATE;
?
-- 啟動(dòng)事務(wù) E
START TRANSACTION;
-- 獲取賬戶2的排他鎖
SELECT * FROM accounts WHERE id = 2 FOR UPDATE;
?
-- 事務(wù) D 試圖獲取賬戶 2 的排他鎖
SELECT * FROM accounts WHERE id = 2 FOR UPDATE;
?
-- 事務(wù) E 試圖獲取賬戶 1 的排他鎖
SELECT * FROM accounts WHERE id = 1 FOR UPDATE;
  • 事務(wù)D已經(jīng)獲得了賬戶1的排他鎖,而事務(wù)E已經(jīng)獲得了賬戶2的排他鎖。
  • 當(dāng)事務(wù)D試圖獲取賬戶2的排他鎖時(shí),它會(huì)被阻塞,同樣地,當(dāng)事務(wù) E 試圖獲取賬戶 1 的排他鎖時(shí)也會(huì)被阻塞。這就會(huì)形成一個(gè)死鎖的情況。
  • InnoDB 會(huì)檢測(cè)到這種情況,并自動(dòng)解決死鎖。它會(huì)選擇一個(gè)事務(wù)回滾,以便另一個(gè)事務(wù)可以繼續(xù)執(zhí)行。
  • 可以通過(guò)查看 INFORMATION_SCHEMA.INNODB_TRX 表來(lái)了解當(dāng)前的事務(wù)狀態(tài),包括死鎖信息

間隙鎖

MySQL 在可重復(fù)讀隔離級(jí)別下是可以解決幻讀問(wèn)題的,解決方案有兩種

  • 可以使用 MVCC方案解決
  • 也可以采用加鎖方案解決。

但是事務(wù)在第一次執(zhí)行讀取操作時(shí),那些幻影記錄尚不存在,我們無(wú)法給這些幻影記錄加上記錄鎖 ,所以InnoDB提出了一種稱之為Gap Locks 的鎖,官方的類型名稱為: LOCK_GAP ,我們可以簡(jiǎn)稱為 gap 鎖 (間隙鎖)

  • 間隙鎖是在行級(jí)別的鎖定之上的一種擴(kuò)展,它不僅鎖定具體的行的兩邊,還鎖定行之間的間隙
  • gap鎖的提出僅僅是為了防止插入幻影記錄而提出的

這種鎖定是為了防止其他事務(wù)插入新的行到已經(jīng)被鎖定的數(shù)據(jù)行之間,從而保證了事務(wù)的隔離性和一致性。

圖中id值為8的記錄加了gap鎖,意味著 不允許別的事務(wù)在id為3記錄后的間隙,即不允許(3,15)之間插入新記錄

類型

插入意向間隙鎖

  • 插入意向間隙鎖告訴其他事務(wù)這里即將發(fā)生插入操作,因此其他事務(wù)不應(yīng)該在該位置進(jìn)行插入

普通間隙鎖

  • 執(zhí)行 SELECT ... FOR UPDATESELECT ... FOR SHARE 查詢時(shí),InnoDB 會(huì)在查詢范圍內(nèi)的所有數(shù)據(jù)行上放置鎖,并在這些行之間的間隙上放置間隙鎖
  • 普通間隙鎖用于防止其他事務(wù)在已鎖定的數(shù)據(jù)行之間插入新行

舉例

-- 有一個(gè)表 orders,其中包含 order_id 和 order_amount 字段
CREATE TABLE orders (
    order_id INT AUTO_INCREMENT PRIMARY KEY,
    order_amount DECIMAL(10, 2)
);
?
INSERT INTO orders (order_amount) VALUES (100), (500), (1000);

現(xiàn)在,我們有兩個(gè)事務(wù) A 和 B。事務(wù) A 執(zhí)行一個(gè)范圍查詢:

-- 啟動(dòng)事務(wù) A
START TRANSACTION;
?
-- 獲取訂單金額在200 到 600之間的排他鎖
SELECT * FROM orders WHERE order_amount BETWEEN 200 AND 600 FOR UPDATE;
?
-- 事務(wù)A在200和600之間的間隙上放置了間隙鎖。這意味著其他事務(wù)不能在這個(gè)范圍內(nèi)插入新的行

示例 2: 插入意向間隙鎖

-- 啟動(dòng)事務(wù) B
START TRANSACTION;
-- 嘗試插入一個(gè)新的訂單
INSERT INTO orders (order_amount) VALUES (300);
#事務(wù)B將被阻塞,因?yàn)樗噲D在事務(wù)A已經(jīng)鎖定的間隙內(nèi)插入新的行

臨鍵鎖

官方的類型名稱為: LOCK_ORDINARY,我們也可以簡(jiǎn)稱為next-key鎖 。Next-Key Locks是在存儲(chǔ)引擎 innodb 、事務(wù)級(jí)別在可重復(fù)讀的情況下使用的數(shù)據(jù)庫(kù)鎖,innodb默認(rèn)的鎖就是臨鍵鎖

Next-Key Locks 是一種組合鎖,它同時(shí)包含了記錄鎖和間隙鎖。簡(jiǎn)單來(lái)說(shuō),會(huì)在一個(gè)記錄上放置一個(gè)記錄鎖,并且在該記錄間隙上放置一個(gè)間隙鎖。

舉例

-- 有一個(gè)表 orders,其中包含 order_id 和 order_amount 字段
CREATE TABLE orders (
    order_id INT AUTO_INCREMENT PRIMARY KEY,
    order_amount DECIMAL(10, 2)
);
INSERT INTO orders (order_amount) VALUES (100), (500), (1000);

現(xiàn)在,我們有兩個(gè)事務(wù)A和B。事務(wù)A執(zhí)行一個(gè)范圍查詢:

-- 啟動(dòng)事務(wù) A
START TRANSACTION;
?
-- 獲取訂單金額在200到600 之間的排他鎖
SELECT * FROM orders WHERE order_amount BETWEEN 200 AND 600 FOR UPDATE;
-- 事務(wù)A會(huì)在 order_amount 為 500 的行上放置一個(gè)排他鎖,并在200和600之間的所有間隙上放置間隙鎖。這意味著其他事務(wù)不能在這個(gè)范圍內(nèi)插入新的行

現(xiàn)在,事務(wù) B 嘗試插入一個(gè)新的訂單:

-- 啟動(dòng)事務(wù) B
START TRANSACTION;
?
-- 嘗試插入一個(gè)新的訂單
INSERT INTO orders (order_amount) VALUES (300);
-- 事務(wù) B 將被阻塞,因?yàn)樗噲D在事務(wù) A 已經(jīng)鎖定的間隙內(nèi)插入新的行

如果您希望避免臨鍵鎖,可以將事務(wù)隔離級(jí)別設(shè)置為 READ COMMITTED

-- 設(shè)置事務(wù)隔離級(jí)別為 READ COMMITTED
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- 啟動(dòng)事務(wù) A
START TRANSACTION;
?
-- 獲取訂單金額在 200 到 600 之間的排他鎖
SELECT * FROM orders WHERE order_amount BETWEEN 200 AND 600 FOR UPDATE;
-- 在這種情況下,事務(wù) A 僅在匹配的行上放置排他鎖,而不會(huì)在行之間的間隙上放置間隙鎖

插入意向鎖

InnoDB規(guī)定:事務(wù)在等待的時(shí)候也需要在內(nèi)存中生成一個(gè)鎖結(jié)構(gòu),表明有事務(wù)想在某個(gè)間隙中插入新記錄,但是現(xiàn)在在等待。InnoDB就把這種類型的鎖命名為 Insert Intention Locks ,官稱為插入意向鎖

工作原理:

假設(shè)事務(wù) T1 對(duì)區(qū)間 [10, 20] 之間的所有行以及這個(gè)區(qū)間的間隙持有 Next-Key Locks。這時(shí),事務(wù) T2 嘗試在區(qū)間 [10, 20] 內(nèi)插入一行數(shù)據(jù),比如插入 15。

  • T2 產(chǎn)生插入意向鎖:由于 T1 持有該間隙上的鎖,T2 無(wú)法立即插入數(shù)據(jù),但它會(huì)在內(nèi)存中創(chuàng)建一個(gè)插入意向鎖,表示它想要在間隙 [10, 20] 中插入數(shù)據(jù)。
  • T2 等待:T2 的插入操作被阻塞,等待 T1 的事務(wù)結(jié)束
  • T1 提交或回滾:當(dāng) T1 完成并釋放其鎖后,T2 的插入意向鎖變?yōu)橛行?/code>,T2 可以繼續(xù)插入數(shù)據(jù)。

小結(jié):

  • 通過(guò)使用插入意向鎖,系統(tǒng)可以更好地管理事務(wù)之間的等待順序,減少死鎖的可能性
  • 它允許事務(wù)聲明插入意圖,并在等待間隙鎖釋放的過(guò)程中保持一定的靈活性

頁(yè)鎖

頁(yè)鎖就是在頁(yè)的粒度上進(jìn)行鎖定,鎖定的數(shù)據(jù)資源比行鎖要多,因?yàn)橐粋€(gè)頁(yè)中可以有多個(gè)行記錄

當(dāng)我們使用頁(yè)鎖的時(shí)候,會(huì)出現(xiàn)數(shù)據(jù)浪費(fèi)的現(xiàn)象,但這樣的浪費(fèi)最多也就是一個(gè)頁(yè)上的數(shù)據(jù)行。頁(yè)鎖的開(kāi)銷介于表鎖和行鎖之間,會(huì)出現(xiàn)死鎖。鎖定粒度介于表鎖和行鎖之間,并發(fā)度一般

加鎖的態(tài)度劃分

樂(lè)觀鎖和悲觀鎖并不是鎖,而是鎖的 設(shè)計(jì)思想,從名字中也可以看出這兩種鎖是兩種看待數(shù)據(jù)并發(fā)的思維方式

①悲觀鎖

顧名思義,就是很悲觀,總是假設(shè)最壞的情況,每次去拿數(shù)據(jù)的時(shí)候都認(rèn)為別人會(huì)修改,所以每次在拿數(shù)據(jù)的時(shí)候都會(huì)上鎖,這樣別人想拿這個(gè)數(shù)據(jù)就會(huì) 阻塞 直到它拿到鎖(共享資源每次只給一個(gè)線程使用,其它線程阻塞,用完后再把資源轉(zhuǎn)讓給其它線程)

比如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖,當(dāng)其他線程想要訪問(wèn)數(shù)據(jù)時(shí),都需要阻塞掛起。Java中 synchronizedReentrantLock 等獨(dú)占鎖就是悲觀鎖思想的實(shí)現(xiàn)

  • 實(shí)現(xiàn)方式: 使用行級(jí)鎖或表級(jí)鎖,例如可以使用 SELECT...FOR UPDATELOCK INSHARE MODE語(yǔ)句來(lái)加鎖。
  • 悲觀鎖適合并發(fā)沖突多,寫多讀少的場(chǎng)景。通過(guò)每次加鎖的形式來(lái)確保數(shù)據(jù)的安全性,吞吐量較低。
-- 讀取數(shù)據(jù)并加鎖
SELECT id, name FROM users WHERE id = 1 FOR UPDATE;
?
-- 執(zhí)行更新操作
UPDATE users SET name = 'new_name' WHERE id = 1;

秒殺案例

其實(shí)就是簡(jiǎn)單的加鎖,解鎖,用戶發(fā)起秒殺請(qǐng)求

  • 檢查庫(kù)存:查詢商品庫(kù)存是否大于0。
  • 獲取悲觀鎖:如果庫(kù)存大于0,則嘗試獲取商品對(duì)應(yīng)的悲觀鎖
  • 扣減庫(kù)存:在鎖定狀態(tài)下,執(zhí)行扣減庫(kù)存的操作。
  • 釋放鎖:成功扣減庫(kù)存后,釋放悲觀鎖。
  • 返回結(jié)果:向用戶返回秒殺成功或失敗的消息。

諸如 還有微服務(wù)中的分布式鎖,其實(shí)也差不多

②樂(lè)觀鎖

認(rèn)為對(duì)同一數(shù)據(jù)的并發(fā)操作不會(huì)總發(fā)生,屬于小概率事件,不用每次都對(duì)數(shù)據(jù)上鎖

但是在更新的時(shí)候會(huì)判斷一下在此期間別人有沒(méi)有去更新這個(gè)數(shù)據(jù),不采用鎖機(jī)制,而是通過(guò)程序來(lái)實(shí)現(xiàn)。在程序上,我們可以采用版本號(hào)機(jī)制或者CAS機(jī)制實(shí)現(xiàn)。樂(lè)觀鎖適用于多讀的應(yīng)用類型,這樣可以提高吞吐量。

-- 假設(shè)有一張用戶表 users,包含 id、name 和 version 字段
-- 讀取數(shù)據(jù)
SELECT id, name, version FROM users WHERE id = 1;
?
-- 更新數(shù)據(jù)時(shí)檢查版本號(hào)
UPDATE users
SET name = 'new_name', version = version + 1
WHERE id = 1 AND version = current_version;

在Java中 java.util.concurrent.atomic 包下的原子變量類就是使用了樂(lè)觀鎖的一種實(shí)現(xiàn)方式:CAS實(shí)現(xiàn)的

適用場(chǎng)景

加鎖的方式劃分

①隱式鎖

顧名思義,看不到的鎖,簡(jiǎn)單來(lái)說(shuō)就是在一個(gè)事務(wù)中執(zhí)行新插入一條記錄操作并不加鎖,但是會(huì)給該插入操作加隱式鎖的結(jié)構(gòu),對(duì)這條插入記錄進(jìn)行保護(hù),防止該記錄被其他事務(wù)訪問(wèn)

案例

-- session 1:
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> insert INTO student VALUES(34,"周八","二班");
Query OK, 1 row affected (0.00 sec)
-- session 2
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from student lock in share mode;   #執(zhí)行完,當(dāng)前事務(wù)被阻塞
mysql> SELECT * FROM performance_schema.data_lock_waits\G;
*************************** 1. row ***************************
             ENGINE: INNODB
   REQUESTING_ENGINE_LOCK_ID: 140562531358232:7:4:9:140562535668584
REQUESTING_ENGINE_TRANSACTION_ID: 422037508068888
     REQUESTING_THREAD_ID: 64
      REQUESTING_EVENT_ID: 6
REQUESTING_OBJECT_INSTANCE_BEGIN: 140562535668584
    BLOCKING_ENGINE_LOCK_ID: 140562531351768:7:4:9:140562535619104
BLOCKING_ENGINE_TRANSACTION_ID: 15902
      BLOCKING_THREAD_ID: 64
       BLOCKING_EVENT_ID: 6
BLOCKING_OBJECT_INSTANCE_BEGIN: 140562535619104
1 row in set (0.00 sec)

分析

  • 上述insert 語(yǔ)句 只是給新插入的那一行上了隱式鎖
  • 后面select * 是給全表上讀鎖
  • 因?yàn)楹竺嬉o全表記錄上鎖,所以前面那條insert 語(yǔ)句會(huì)將那一行的隱式鎖轉(zhuǎn)行為X鎖
  • 所以后面的 select語(yǔ)句的 讀鎖 會(huì)和insert 語(yǔ)句生成的X鎖沖突,所以select語(yǔ)句等待
  • 如果select語(yǔ)句 不是 select * 全表記錄 ,而是 select 其他的已存在索引上的等值記錄,那么就不會(huì)和insert 語(yǔ)句X鎖 沖突,則可以查詢成功

隱式鎖的邏輯過(guò)程如下

  • A. InnoDB目錄頁(yè)中的每條記錄中都一個(gè)隱含的trx_id字段,這個(gè)字段存在于聚簇索引的B+Tree中。
  • B. 在操作一條記錄前,首先根據(jù)記錄中的trx_id檢查該事務(wù)是否是活動(dòng)的事務(wù)(未提交或回滾)。如果是活動(dòng)的事務(wù),首先將隱式鎖轉(zhuǎn)換為顯式鎖 (就是為該事務(wù)添加一個(gè)鎖)
  • C. 檢查是否有鎖沖突,如果有沖突,創(chuàng)建鎖,并設(shè)置為waiting狀態(tài)。如果沒(méi)有沖突不加鎖,跳到E。
  • D. 等待加鎖成功,被喚醒,或者超時(shí)
  • E. 寫數(shù)據(jù),并將自己的trx_id寫入trx_id字段

如何判斷隱式鎖是否存在

InnoDB的每條記錄中都一個(gè)隱含的trx_id字段,這個(gè)字段存在于聚集索引的B+Tree中。假設(shè)只有主鍵索引,則在進(jìn)行插入時(shí),行數(shù)據(jù)的trx_id被設(shè)置為當(dāng)前事務(wù)id;假設(shè)存在二級(jí)索引,則在對(duì)二級(jí)索引進(jìn)行插入時(shí),需要更新所在page的max_trx_id。

因此對(duì)于主鍵,只需要通過(guò)查看記錄隱藏列trx_id是否是活躍事務(wù)就可以判斷隱式鎖是否存在。 對(duì)于對(duì)于二級(jí)索引會(huì)相對(duì)比較麻煩,先通過(guò)二級(jí)索引頁(yè)上的max_trx_id進(jìn)行過(guò)濾,如果無(wú)法判斷是否活躍則需要通過(guò)應(yīng)用undo日志回溯老版本數(shù)據(jù),才能進(jìn)行準(zhǔn)確的判斷。

②顯式鎖

通過(guò)特定的語(yǔ)句進(jìn)行加鎖,例如:

#顯示加共享鎖
select ....  lock in share mode
#顯示加排它鎖
select ....  for update

其它鎖

全局鎖

就是對(duì)整個(gè)數(shù)據(jù)庫(kù)實(shí)例 加鎖。當(dāng)你需要讓整個(gè)庫(kù)處于 只讀狀態(tài) 的時(shí)候,可以使用這個(gè)命令,之后其他線程的以下語(yǔ)句會(huì)被阻塞:數(shù)據(jù)更新語(yǔ)句(數(shù)據(jù)的增刪改)、數(shù)據(jù)定義語(yǔ)句(包括建表、修改表結(jié)構(gòu)等)和更新類事務(wù)的提交語(yǔ)句-全局鎖的典型使用 場(chǎng)景 是:做 全庫(kù)邏輯備份

Flush tables with read lock

死鎖

死鎖是指兩個(gè)或多個(gè)事務(wù)在同一資源上相互占用,并請(qǐng)求鎖定對(duì)方占用的資源,從而導(dǎo)致惡性循環(huán)。

有 兩種解決策略:

  • 直接進(jìn)入等待,直到超時(shí)。這個(gè)超時(shí)時(shí)間可以通過(guò)參數(shù)innodb_lock_wait_timeout 來(lái)設(shè)置
  • 另一種策略是,發(fā)起死鎖檢測(cè),發(fā)現(xiàn)死鎖后,主動(dòng)回滾死鎖鏈條中的某一個(gè)事務(wù)(將持有最少行級(jí)排他鎖的事務(wù)進(jìn)行回滾),讓其他事務(wù)得以繼續(xù)執(zhí)行。將參數(shù)innodb_deadlock_detect設(shè)置為on,表示開(kāi)啟這個(gè)邏輯

總結(jié)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • MySQL 邏輯備份與恢復(fù)測(cè)試的相關(guān)總結(jié)

    MySQL 邏輯備份與恢復(fù)測(cè)試的相關(guān)總結(jié)

    數(shù)據(jù)庫(kù)邏輯備份就是備份軟件按照我們最初所設(shè)計(jì)的邏輯關(guān)系,以數(shù)據(jù)庫(kù)的邏輯結(jié)構(gòu)對(duì)象為單位,將數(shù)據(jù)庫(kù)中的數(shù)據(jù)按照預(yù)定義的邏輯關(guān)聯(lián)格式一條一條生成相關(guān)的文本文件,以達(dá)到備份的目的。本文將具體介紹MySQL 邏輯備份的相關(guān)概念及如何做恢復(fù)測(cè)試。
    2021-05-05
  • 簡(jiǎn)單的MySQL備份與還原方法分享

    簡(jiǎn)單的MySQL備份與還原方法分享

    這篇文章主要介紹了簡(jiǎn)單的MySQL備份與還原方法,文中Linux與Windows的兩種情況都有介紹,需要的朋友可以參考下
    2015-08-08
  • mysql事務(wù)隔離級(jí)別詳情

    mysql事務(wù)隔離級(jí)別詳情

    這篇文章主要介紹了mysql事務(wù)隔離級(jí)別,文章圍繞mysql事務(wù)隔離的相關(guān)資料詳細(xì)展開(kāi)文章重點(diǎn),需要的小伙伴可以參考一下,希望對(duì)你有所幫助
    2021-10-10
  • MySQL優(yōu)化insert性能的方法示例

    MySQL優(yōu)化insert性能的方法示例

    對(duì)于一些數(shù)據(jù)量較大的系統(tǒng),數(shù)據(jù)庫(kù)面臨的問(wèn)題除了查詢效率低下,還有就是數(shù)據(jù)入庫(kù)時(shí)間長(zhǎng)。下面這篇文章主要給大家介紹了關(guān)于MySQL優(yōu)化insert性能的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2018-06-06
  • 關(guān)于TIMESTAMP with implicit DEFAULT value is deprecated 錯(cuò)誤解決方法

    關(guān)于TIMESTAMP with implicit DEFAULT value&

    本文介紹了“TIMESTAMP with implicit DEFAULT value is deprecated”錯(cuò)誤的原因及解決方法,解決方法包括顯式指定默認(rèn)值、修改字段類型、更新數(shù)據(jù)庫(kù)版本或?qū)で髱椭?感興趣的朋友一起看看吧
    2025-02-02
  • mysql安裝圖解 mysql圖文安裝教程(詳細(xì)說(shuō)明)

    mysql安裝圖解 mysql圖文安裝教程(詳細(xì)說(shuō)明)

    很多朋友剛開(kāi)始接觸mysql數(shù)據(jù)庫(kù)服務(wù)器,下面是網(wǎng)友整理的一篇mysql的安裝教程,步驟明細(xì)也有詳細(xì)的說(shuō)明。
    2010-06-06
  • 關(guān)于MySQL死鎖的產(chǎn)生原因、檢測(cè)與解決方式

    關(guān)于MySQL死鎖的產(chǎn)生原因、檢測(cè)與解決方式

    這篇文章主要介紹了關(guān)于MySQL死鎖的產(chǎn)生原因、檢測(cè)與解決方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-07-07
  • 解決MySQL錯(cuò)誤碼:1054 Unknown column ‘**‘ in ‘field list‘的問(wèn)題

    解決MySQL錯(cuò)誤碼:1054 Unknown column ‘**‘ in&n

    這篇文章主要介紹了解決MySQL錯(cuò)誤碼:1054 Unknown column ‘**‘ in ‘field list‘的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • MySQL中的常用樹(shù)形結(jié)構(gòu)設(shè)計(jì)總結(jié)

    MySQL中的常用樹(shù)形結(jié)構(gòu)設(shè)計(jì)總結(jié)

    這篇文章主要介紹了MySQL中的常用樹(shù)形結(jié)構(gòu)設(shè)計(jì)總結(jié),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • 阿里云ECS centos6.8下安裝配置MySql5.7的教程

    阿里云ECS centos6.8下安裝配置MySql5.7的教程

    阿里云默認(rèn)yum命令下的MySQL是5.17****,安裝mysql5.7之前先卸載以前的版本。下面通過(guò)本文給大家介紹阿里云ECS centos6.8下安裝配置MySql5.7的教程,需要的的朋友參考下吧
    2017-07-07

最新評(píng)論