MySQL?事務(wù)隔離性及鎖處理方式
一、事務(wù)隔離性概述
事務(wù)隔離性(Isolation) 是指多個并發(fā)事務(wù)之間相互隔離,一個事務(wù)的執(zhí)行不應(yīng)該影響其他事務(wù)的執(zhí)行。
MySQL 支持四種事務(wù)隔離級別,默認(rèn)隔離級別:可重復(fù)讀(Repeatable Read)。

鎖的行為與事務(wù)的隔離級別緊密相關(guān):
讀未提交:通常不加鎖,通過直接讀最新數(shù)據(jù)實現(xiàn),存在臟讀、幻讀等問題。
讀已提交:每次讀取的都是已提交的最新數(shù)據(jù)(快照)。 對于 UPDATE、DELETE,只對實際修改的行加鎖(記錄鎖),不使用間隙鎖,所無法避免幻讀。
可重復(fù)讀(InnoDB 的默認(rèn)級別):在事務(wù)開始時創(chuàng)建一致性視圖,整個事務(wù)期間都讀取這個視圖。使用臨鍵鎖作為默認(rèn)的行鎖算法,通過鎖住記錄和間隙來防止幻讀。
串行化:最高的隔離級別,通過強(qiáng)制事務(wù)串行執(zhí)行來避免所有并發(fā)問題。 在這個級別下,InnoDB 可能會隱式地將普通的 SELECT 語句轉(zhuǎn)換為 SELECT ... FORSHARE,從而加共享鎖,導(dǎo)致讀讀也可能阻塞。
二、MySQL 保證隔離性的機(jī)制
MySQL 的鎖機(jī)制是為了在并發(fā)訪問下,保證數(shù)據(jù)的一致性、完整性而設(shè)計的。它可以大致分為以下三個維度來理解:
- 鎖的粒度:鎖定的范圍大小
- 鎖的模式:鎖的兼容性,即多個鎖之間能否共存
- 鎖的類型:從程序員視角看,鎖的共享與排他特性
按鎖的粒度劃分
1 全局鎖,作用范圍:整個數(shù)據(jù)庫實例。
FLUSH TABLES WITH READ LOCK; SET read_only = ON
效果:使數(shù)據(jù)庫處于只讀狀態(tài),所有數(shù)據(jù)修改操作(DML)和數(shù)據(jù)結(jié)構(gòu)變更操作 (DDL)都會被阻塞。
使用場景:全庫邏輯備份。但請注意,在 InnoDB 中,由于有 MVCC,通常使 用 mysqldump --single-transaction 來進(jìn)行不鎖表的熱備份,這比全局鎖更好。
2 表級鎖,作用范圍:整張表
LOCK TABLES table_name READ/WRITE; 和 UNLOCK TABLES;
MyISAM 引擎默認(rèn)使用表鎖。
元數(shù)據(jù)鎖:不需要顯式使用,在執(zhí)行 DML(如 SELECT, INSERT, UPDATE, DELETE)時自動加,防止表結(jié)構(gòu)被修改。讀鎖之間不互斥,但寫鎖是排他的。
意向鎖:InnoDB 特有的表級鎖,用于快速判斷表內(nèi)是否有行鎖沖突。
效果:鎖定整張表,粒度大,并發(fā)性能差。
3 行級鎖, 作用范圍:單行或多行記錄
支持引擎:InnoDB。
效果:粒度最小,只鎖定需要操作的行,極大提高了并發(fā)性能。但管理開銷最大,容易 導(dǎo)致死鎖。
實現(xiàn)方式:InnoDB 通過給索引項加鎖來實現(xiàn)行鎖。這意味著:如果查詢條件沒有用到索引,InnoDB 會退化為表鎖,行鎖實際上是加在索引記錄上的。
InnoDB 的行鎖模式(鎖的類型)
1 共享鎖,簡稱:S 鎖。
特性:又稱為讀鎖。一個事務(wù)獲取了某行的共享鎖后,其他事務(wù)也可以獲取同一行的共享鎖(讀讀兼容),但不能獲取該行的排他鎖(讀寫不兼容)。
加鎖方式:
SELECT ... LOCK IN SHARE MODE; -- 在舊版本中 SELECT ... FOR SHARE; -- 在 MySQL 8.0+ 中推薦使用
2 排他鎖,簡稱:X 鎖。
特性:又稱為寫鎖。一個事務(wù)獲取了某行的排他鎖后,其他事務(wù)既不能獲取該行的共享鎖,也不能獲取該行的排他鎖(寫寫、讀寫都不兼容)。
加鎖方式:
SELECT ... FOR UPDATE;
DML 語句(UPDATE, DELETE, INSERT)會自動給涉及的行加上排他鎖。
兼容性矩陣:

三、行鎖的算法(鎖定了哪些數(shù)據(jù))
InnoDB 的行鎖是通過對索引項加鎖實現(xiàn)的,根據(jù)查詢條件和索引使用情況,鎖定的范圍有所不同。
3.1 記錄鎖
鎖定:單個索引記錄。
場景:當(dāng)語句精確匹配到某一條記錄,且使用了唯一索引(包括主鍵)時。
示例:
SELECT * FROM users WHERE id = 10 FOR UPDATE; 如果 id 是主鍵,這條語句會在 id=10 這條索引記錄上加 X 鎖。
3.2 間隙鎖
鎖定:一個索引區(qū)間,但不包括記錄本身。例如鎖住 (5, 10) 這個開區(qū)間。
目的:防止其他事務(wù)在區(qū)間內(nèi)插入新的記錄,從而解決幻讀問題。
場景:使用范圍查詢或者查詢不存在的記錄時。
示例:
SELECT * FROM users WHERE id BETWEEN 5 AND 10 FOR UPDATE; 這條語句會鎖住 id 在 (5, 10) 區(qū)間內(nèi)的所有“間隙”,防止插入 id=6,7,8,9 的新記錄。
3.3 臨鍵鎖
鎖定:一個索引記錄 + 該記錄之前的間隙。它是 記錄鎖 + 間隙鎖 的組合。
目的:InnoDB 默認(rèn)的行鎖算法。它既鎖住了記錄本身,也鎖住了它之前的間隙,從在 “可重復(fù)讀”隔離級別下有效地防止了幻讀。
示例:如果表中有記錄 id=5, 10, 15。
SELECT * FROM users WHERE id > 10 AND id <= 15 FOR UPDATE; 這條語句會鎖定 (10, 15] 這個區(qū)間。它鎖住了 id=15 這條記錄(記錄鎖), 以及 (10,15) 這個間隙(間隙鎖)。
四、 死鎖
當(dāng)兩個或多個事務(wù)相互等待對方釋放鎖時,就會發(fā)生死鎖。
示例:
事務(wù) A:UPDATE table SET ... WHERE id = 1; (持有 id=1 的 X 鎖)
事務(wù) B:UPDATE table SET ... WHERE id = 2; (持有 id=2 的 X 鎖)
事務(wù) A:UPDATE table SET ... WHERE id = 2; (等待事務(wù) B 釋放 id=2 的鎖)
事務(wù) B:UPDATE table SET ... WHERE id = 1; (等待事務(wù) A 釋放 id=1 的鎖)
-> 死鎖發(fā)生!
InnoDB 的處理方式:
InnoDB 有一個內(nèi)置的死鎖檢測機(jī)制,會主動選擇一個回滾成本較小的事務(wù)(通常就是影響行數(shù)較少的事務(wù))進(jìn)行回滾,并報出 1213 錯誤(Deadlock found when trying to get lock)。
另一個事務(wù)則可以繼續(xù)執(zhí)行。
如何避免死鎖:
盡量讓事務(wù)以相同的順序訪問表和行。
在事務(wù)中,盡量一次鎖定所有需要的資源,減少事務(wù)大小。
使用較低的隔離級別(如 Read Committed)可以減少間隙鎖的使用,從而降低死鎖概率。
為表添加合理的索引,避免全表掃描導(dǎo)致鎖表。
到此這篇關(guān)于MySQL 事務(wù)隔離性及鎖的文章就介紹到這了,更多相關(guān)mysql內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MySQL實現(xiàn)導(dǎo)出excel的方法分析
這篇文章主要介紹了MySQL實現(xiàn)導(dǎo)出excel的方法,結(jié)合實例形式分析了基于php、js等方法實現(xiàn)mysql導(dǎo)出Excel的相關(guān)操作技巧,需要的朋友可以參考下2018-03-03
mysql查線上數(shù)據(jù)注意數(shù)據(jù)庫的隔離級別
數(shù)據(jù)庫的隔離級別關(guān)乎事務(wù)對其他并發(fā)事務(wù)的可見性及其對數(shù)據(jù)庫的影響,隔離級別的選擇決定了并發(fā)性能和數(shù)據(jù)一致性的平衡,SQL標(biāo)準(zhǔn)定義了四種隔離級別,每種級別都有不同的應(yīng)用場景和防止并發(fā)問題的能力,感興趣的可以了解一下2024-10-10
MySQL數(shù)據(jù)庫修復(fù)方法(MyISAM/InnoDB)
這篇文章主要為大家整理了7條修復(fù)MySQL數(shù)據(jù)庫的方法,當(dāng)簡單的重啟對數(shù)據(jù)庫不起作用,或者有表崩潰時,需要的朋友可以參考下2014-06-06
分析Mysql事務(wù)和數(shù)據(jù)的一致性處理問題
本篇文章主要給大家介紹了Mysql事務(wù)和數(shù)據(jù)的一致性處理問題,跟著小編一起學(xué)習(xí)下吧。2017-12-12

