目錄
1、MySQL讀取定義
1.1、鎖的分類
1.2、快照讀與當前讀
1.3、使用場景
1.4、區別
2、實現機制
2.1、實現原理
2.2、隔離級別和快照聯系
1、隔離級別
2、快照讀
2.3、快照何時生成
3、SQL場景實現
3.1、快照讀
3.2、當前讀
4、鎖的細節(與當前讀相關)
5、影響 / 并發行為
5.1、快照讀:
5.2、當前讀:
6、注意事項與常見誤區
前言
????????MySQL讀取(主要指 InnoDB 存儲引擎)中“快照讀(snapshot read,也叫一致性讀/consistent read)”和“當前讀(current read)”。
如下所示:
????????這兩種讀取方式在事務隔離級別、并發控制和數據一致性方面有著本質的區別。主要區別在于是否加鎖以及數據一致性。
1、MySQL讀取定義
1.1、鎖的分類
1、共享鎖(S鎖):
? ?共享 (S) 用于不更改或不更新數據的操作(只讀操作),如 SELECT 語句。
????????如果事務T僅對數據A進行讀取,那么會對數據A加上共享鎖,之后則其他事務如果要讀取數據A的話可以對其繼續加共享鎖,但是不能加排他鎖(也就是無法修改數據)。獲準共享鎖的事務只能讀數據,不能修改數據。
2、排他鎖(X鎖):
????????用于數據修改操作,例如 INSERT、UPDATE 或 DELETE。確保不會同時同一資源進行多重更新。
????????如果事務T對數據A要進行修改,則需要對其添加排它鎖,加上排他鎖后,則其他事務不能再對A加任任何類型的封鎖。獲準排他鎖的事務既能讀數據,又能修改數據。
SQL代碼示例:
共享鎖
select * from table id = 1 lock in share mode;排他鎖
select * from table where id = 1 for update;
1.2、快照讀與當前讀
1、快照讀
????????(Snapshot Read / Consistent Read):基于事務快照(MVCC),讀取的是“某個時間點”的已提交版本,不加鎖、非阻塞,多個讀返回一致的歷史版本。
?REPEATABLE READ 下(基于事務開始時的快照):同一事務內多次 SELECT 返回相同結果;
?READ COMMITTED 下每個語句建立新的快照。
SQL示例如下:
-- 事務 A
START TRANSACTION;
SELECT * FROM users WHERE id = 1; -- 快照讀,讀取事務開始時的數據-- 事務 B
START TRANSACTION;
UPDATE users SET name = 'Alice' WHERE id = 1; -- 更新數據并提交
COMMIT;-- 事務 A
SELECT * FROM users WHERE id = 1; -- 仍然讀取事務開始時的數據(快照讀)
COMMIT;
2、當前讀
????????(Current Read):直接讀取當前最新數據(buffer pool/磁盤)并且讀取之后還要保證其他并發事務不能修改當前記錄,對讀取的記錄加鎖。
當前讀:select…lock in share mode,select…for update。
當前讀:update,delete,insert。
SQL代碼示例如下:
-- 事務 A
START TRANSACTION;
SELECT * FROM users WHERE id = 1 FOR UPDATE; -- 當前讀,獲取最新數據并加鎖-- 事務 B
START TRANSACTION;
UPDATE users SET name = 'Alice' WHERE id = 1; -- 被阻塞,等待事務 A 釋放鎖-- 事務 A
COMMIT; -- 提交后,事務 B 的更新操作繼續執行
1.3、使用場景
1、當前讀適用場景
- 需要獲取最新數據的實時查詢
- 需要保證數據一致性的金融交易
- 先讀后寫的業務邏輯
- 需要防止并發修改的場景
- 只讀查詢(報表、數據分析)
- 對實時性要求不高的查詢
- 大查詢,不希望阻塞其他操作
- 需要高并發的讀場景
1.4、區別
如下所示:
?
2、實現機制
2.1、實現原理
1、快照讀:通過MVCC+undolog實現;
????????如果最新版本不是當前事務可見,InnoDB 會從 undo log 找到符合快照時間點的版本(所以快照讀可能讀取 undo 中的舊版本)。不會設置鎖。
????????不同事務或者相同事務的對同一記錄的修改,會導致該記錄的undo log成為一條記錄版本線性表,既鏈表,undo log的鏈首就是最新的舊記錄,鏈尾就是最早的舊記錄。
????????所以快照讀都是去讀取undolog中鏈首的最新的舊記錄。
更多mvcc和undolog的介紹,可參考:探討LRU和MVCC在場景的實踐-CSDN博客文章瀏覽閱讀827次,點贊33次,收藏29次。MVCC 可以有效防止臟讀,因為它提供一致的快照顯示已提交修改的數據。它通過版本控制和快照隔離處理多用戶并發訪問,從而提高數據庫的高并發性能。1、徹底拿下InnoDB的MVCC快照機制_innodb 的 快照讀 如何提取快照-CSDN博客。https://dyclt.blog.csdn.net/article/details/147588118?spm=1011.2415.3001.5331
2、當前讀:通過共享鎖+排他鎖+Next-Key Lock實現;
當前讀通過next-key鎖(記錄鎖+間隙鎖)來實現:行鎖(Record Lock):鎖定索引記錄
間隙鎖(Gap Lock):鎖定索引記錄之間的間隙
next-key鎖:行鎖+間隙鎖,鎖定一個范圍并鎖定記錄本身
例如執行DELETE FROM T WHERE age = 7;時,MySQL會在age索引上加鎖(4,10)區間,防止其他事務
插入age=7的記錄,從而避免幻讀問題。
如下所示:
- 每次對行數據進行讀取的時候,加共享鎖。此時就不允許修改,但是允許其他事務讀取,所以每次都可以讀到最新的數據。
- 每次對行數據進行修改的時候,加排他鎖,不允許其他事務讀取和修改。這種情況下其他事務讀取的數據也一定是最新的數據。
- 每次對范圍行數據進行讀取的時候,對這個范圍加一個范圍共享鎖。
- 每次對范圍行數據進行修改的時候,讀這個范圍加一個范圍排它鎖。
- 基于上述鎖機制,實現當前讀,確保每次讀取的都是最新的數據。
??注意:
????????next-key包括兩部分:行鎖和間隙鎖。行鎖是加在索引上的鎖(而非數據行),間隙鎖是加在索引之間的。
2.2、隔離級別和快照聯系
1、隔離級別
1、讀未提交(READ UNCOMMITTED)
快照讀:不存在,總是讀取最新數據(包括未提交的)。
當前讀:讀取最新已提交數據并加鎖。
2、讀已提交(READ COMMITTED)
快照讀:每次SELECT都會生成新的ReadView,讀取最新已提交數據。
當前讀:讀取最新已提交數據并加鎖。
3、可重復讀(REPEATABLE READ)
快照讀:事務第一次SELECT時生成ReadView,后續復用,保證讀取一致性。
當前讀:讀取最新已提交數據并加鎖。
4、串行化(SERIALIZABLE)
快照讀:退化為當前讀(加共享鎖)。
當前讀:讀取最新已提交數據并加鎖。
2、快照讀
REPEATABLE READ(默認):
????????事務開始時建立一次快照,整個事務內普通 SELECT 都基于這個快照(repeatable)。
READ COMMITTED:
????????每個語句單獨建立快照(statement-level),因此同一事務內不同語句可能看到不同已提交數據(避免臟讀,但允許不可重復讀)。
Undo log 提供歷史版本;如果舊版本被 purge 掉,讀取老快照可能失敗。
2.3、快照何時生成
1、在讀未提交隔離級別下,快照是什么時候生成的?
沒有快照,因為不需要,怎么讀都讀到最新的,不管是否提交。
2、在讀已提交隔離級別下,快照是什么時候生成的?
SQL語句開始執行的時候。
3、在可重復讀隔離級別下,快照是什么時候生成的?
????????事務開始的時候(可能會有很多條select SQL語句執行,快照生命周期是到事務結束的時候)
4、在串行化隔離級別下,快照是什么時候生成的?
“寫”會加“寫鎖”,“讀”會加“讀鎖”,讀的的數據都是當前最新的數據(沒有快照,當前讀)
3、SQL場景實現
3.1、快照讀
1、?快照讀不會看到后續提交(REPEATABLE READ):事務級快照讀
事務 T1:
BEGIN;? ?? ? SELECT value FROM t WHERE id=1;?
-- 假設值為 A (從快照讀)(此時 T1 未提交,繼續執行)
事務 T2:
BEGIN;? ? ? ?UPDATE t SET value='B' WHERE id=1;? ? ? ? COMMIT;
回到 T1:在 REPEATABLE READ 下仍然看到 A(快照不變)
SELECT value FROM t WHERE id=1;? ?
COMMIT;
2、 READ COMMITTED 下快照行為(每語句快照):語句級快照讀
事務 T1:
BEGIN;? ? ?SELECT value FROM t WHERE id=1;
-- 看到 A
事務 T2:
BEGIN;? ? ? ?UPDATE t SET value='B' WHERE id=1;? ? ? ? COMMIT;
T1 再次執行:
SELECT value FROM t WHERE id=1;
-- 在 READ COMMITTED 下可能看到 B(新的語句快照)
3.2、當前讀
1、當前讀會立即看到并與寫沖突/加鎖:
事務 T1:
BEGIN;????????SELECT value FROM t WHERE id=1 FOR UPDATE;
-- 當前讀并對該行加排它鎖(此時 T1 鎖住該行)
事務 T2(并發執行):
BEGIN;? ?UPDATE t SET value='B' WHERE id=1;? ? ? ?COMMIT;
-- 將在 T1 提交前被阻塞,直到 T1 提交或回滾
4、鎖的細節(與當前讀相關)
更多關于mysql的鎖介紹:MySQL的事務和鎖機制的詳細介紹_mysql鎖機制與事物-CSDN博客文章瀏覽閱讀1.3k次,點贊47次,收藏10次。MySQL事務與鎖機制詳解 摘要: 本文詳細解析MySQL的事務特性和鎖機制。首先介紹事務的ACID特性(原子性、一致性、隔離性、持久性)和四種隔離級別(讀未提交、讀提交、可重復讀、串行化),重點分析多線程并發事務可能產生的臟讀、不可重復讀和幻讀問題。其次深入探討MVCC多版本并發控制原理及其解決并發問題的方式。然后系統講解MySQL鎖的分類,包括獨占鎖、共享鎖及其兼容性,以及InnoDB引擎特有的記錄鎖、間隙鎖、臨鍵鎖等實現細節。最后提供鎖監控方法和優化建議,包括縮短事務長度、合理設計索引等,并比較不同隔_mysql鎖機制與事物https://dyclt.blog.csdn.net/article/details/149141972?spm=1011.2415.3001.5331
1、SELECT ... FOR UPDATE:
????????對選中的記錄加排它鎖(record lock),在 REPEATABLE READ 下通常是 next?key lock(record+gap),能防幻讀;在 READ COMMITTED 下可能只加 record lock(實現差異)。
2、SELECT ... LOCK IN SHARE MODE:
共享鎖,允許并發讀取但阻止寫入。
UPDATE/DELETE 語句本質是當前讀(讀取并鎖定匹配行),而非快照讀。
5、影響 / 并發行為
5.1、快照讀:
不阻塞其他事務的寫操作(不會加鎖)。
不被子后續提交所影響(在 REPEATABLE READ 內)不會阻塞寫者,但寫者提交不會讓已存在事務的快照變更。
5.2、當前讀:
會加鎖,可能阻塞其他事務(或被其他事務阻塞),用于實現悲觀鎖定和防止幻讀/并發沖突。SELECT ... FOR UPDATE 會鎖定讀取到的記錄(并可能使用 next-key lock 以避免幻讀,取決于隔離級別和索引類型)。
6、注意事項與常見誤區
1、 “快照讀不會造成任何索引訪問” 不準確:
????????快照讀也會走索引來定位行,但讀取邏輯是基于版本可見性(從 undo 中回溯),它不設置鎖。
2、長事務會保留較多 undo 數據:
影響 undo log 大小并增加 purge 壓力;長時間的快照讀(長事務)會阻止舊版本被清理。
3、不穩定結果
????????與快照/當前讀無直接關系,但與隔離級別和鎖有關,使用 skip/limit 的分頁在并發寫入下可能產生。
總結
????????普通 SELECT(無 FOR UPDATE/LOCK)是快照讀(基于 MVCC,非鎖定),在 REPEATABLE READ 下為事務級快照,同一事務內多次 SELECT 返回一致結果;READ COMMITTED 下每語句建立快照。
????????SELECT ... FOR UPDATE / UPDATE / DELETE 等是當前讀,會讀取最新數據并加鎖,可能阻塞其他事務寫入。
????????選擇隔離級別與是否加鎖,需要在“數據一致性需求(可重復讀/防止幻讀)”與“并發性能(鎖競爭/阻塞)”之間權衡。
參考文章:
1、Mysql中的快照讀和當前讀_mysql快照讀-CSDN博客文章瀏覽閱讀1k次,點贊9次,收藏10次。本文介紹了MySQL的兩種讀取模式:當前讀和快照讀。當前讀通過共享鎖、排他鎖和Next - Key Lock實現,每次讀取最新數據;快照讀通過MVCC和undolog實現,讀寫不沖突。還闡述了相關知識,如undolog、共享鎖和排他鎖,并分析了不同隔離級別下快照讀的差異。https://blog.csdn.net/aberwang9/article/details/135185331?ops_request_misc=&request_id=&biz_id=102&utm_term=mysql%E7%9A%84%E5%BD%93%E5%89%8D%E8%AF%BB%E5%92%8C%E5%BF%AB%E7%85%A7%E8%AF%BB&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-0-135185331.142^v102^control&spm=1018.2226.3001.4187
2、【MYSQL】當前讀和快照讀_mysql 當前讀-CSDN博客文章瀏覽閱讀821次,點贊3次,收藏9次。復習下隔離級別:1、讀未提交:一個事務還沒提交時,它做的變更就能被別的事務看到。2、讀提交:一個事務提交之后,它做的變更會被其他事務看到3、可重復讀:一個事務執行過程中看到的數據,總是跟這個事務在啟動時看到的數據是一致的。未提交的數據對其他事務不可見4、串行化:對于同一行記錄,“寫”會加“寫鎖”,“讀”會加“讀鎖”。當出現讀寫鎖沖突的時候,后訪問的事務必須等前一個事務執行完成,才能繼續執行。_mysql 當前讀https://blog.csdn.net/xiazi0721/article/details/141175805?ops_request_misc=&request_id=&biz_id=102&utm_term=mysql%E7%9A%84%E5%BD%93%E5%89%8D%E8%AF%BB%E5%92%8C%E5%BF%AB%E7%85%A7%E8%AF%BB&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-1-141175805.142^v102^control&spm=1018.2226.3001.4187