InnoDB解決幻讀的方法詳解
前言
大部分人在日常的業(yè)務開發(fā)中,其實很少去關注數(shù)據(jù)庫的事務相關問題,基本上都是 CURD 一把梭。正好最近在看 MySQL 的相關基礎知識,其中對于幻讀問題之前一直沒有理解深刻,今天就來聊聊「InnoDB 是如何解決幻讀的」,話不多說,下面進入主題。
事務隔離級別
事務隔離是數(shù)據(jù)庫處理的基礎之一,是 ACID 中的 I
。在 MySQL 的 InnoDB 引擎中支持在 SQL:1992 標準中的四種事務隔離級別,如下圖所示,其中 P1 表示臟讀(Dirty read),P2 表示不可重復讀(Dirty read),P3 表示幻讀(Phantom)。
為什么需要定義這么多隔離呢?從上圖中也能猜出一二了,InnoDB 提供多個隔離級別主要原因是:讓使用者可以在多個事務同時進行更改和執(zhí)行查詢時微調(diào)性能與結果的可靠性、一致性和可再現(xiàn)性之間的平衡的設置。是一種性能與結果可靠性間的 trade off
。
什么是幻讀
在聊「InnoDB 解決幻讀方式」前我們需要先了解幻讀是什么,官方文檔的描述如下:
A row that appears in the result set of a query, but not in the result set of an earlier query.
其中我加粗的「result set」是關鍵的地方,兩次查詢返回的是結果集,說明必須是一個范圍查詢操作??偨Y下,幻讀就是:在同一個事務中,在前后兩次查詢相同范圍時,兩次查詢得到的結果是不一致的。所以幻讀會產(chǎn)生數(shù)據(jù)一致性問題。
InnoDB 解決幻讀方式
為了解決上述的幻讀問題,InnoDB 引入了兩種鎖,分別是「間隙鎖」和「next-key 鎖」。下面通過一個示例來描述這兩種鎖的作用分別是什么。假如存在一個這樣的 B+ Tree 的索引結構,結構中有 4 個索引元素分別是:9527、9530、9535、9540。
此時當我們使用如下 SQL 通過主鍵索引查詢一條記錄,并且加上 X 鎖(排它鎖)時:
select * from user where id = 9527 for update;
這時就會產(chǎn)生一個記錄鎖(也就是行鎖),鎖定 id = 9527
這個索引。
在被鎖定的記錄(這里是 id = 9527)的鎖釋放之前,其它事務無法對這條被鎖定記錄做任何操作。再回憶一下,前面說的幻讀定義「在同一個事務中,在前后兩次查詢相同范圍時,兩次查詢得到的結果是不一致」。注意,這里強調(diào)的是范圍查詢。
InnoDB 要解決幻讀問題,就必須得保證在如果在一個事務中,通過如下這條語句進行鎖定時:
select * from user where id > 9530 and id < 9535 for update;
此時,另外一個語句再執(zhí)行一如下這條 insert 語句時,需要被阻塞,直到上面這個獲得鎖的事務釋放鎖后才能執(zhí)行。
insert into user(id, name, age) values(9533, 'Jack', 44);
為此,InnoDB 引入了「間隙鎖」,它的主要功能是鎖定一段范圍內(nèi)的索引記錄。比如上面查詢 id > 9530 and id < 9535
的時候,對 B+ Tree 中的(9530,9535)這個開區(qū)間范圍的索引加間隙鎖。
在這種加了間隙鎖的情況下,其它事務對這個區(qū)間的數(shù)據(jù)進行插入、更新、刪除都會被鎖住直到這個獲取到鎖的事務釋放。
這種是在區(qū)間之間的情況,你可能想到另外的一種情況:鎖定多個區(qū)間,如下的一條語句:
select * from user where id > 9530 for update;
上面這條查詢語句是針對 id > 9530
這個條件加鎖,那么此時它需要鎖定多個索引區(qū)間,所以在這種情況下 InnoDB 引入了「next-key 鎖」機制。其實 next-key 鎖的效果相當于間隙鎖和記錄鎖的合集,記錄鎖鎖定存在的記錄行,間隙鎖鎖住記錄行之間的間隙,而 next-key 鎖它鎖住的是兩者之和。
在 InnoDB 中,每個數(shù)據(jù)行上的非唯一索引列上都會存在一把 next-key 鎖,當某個事務持有該數(shù)據(jù)行的 next-key 鎖時,會鎖住一段左開右閉區(qū)間的數(shù)據(jù)。因此,當通過 id > 9530
這樣一種范圍查詢加鎖時,會加 next-key 鎖,鎖定區(qū)間是范圍是:
(9530,9535] (9535,9540] (9540,+∞]
間隙鎖(也叫 Gap 鎖)和 next-key 鎖的區(qū)別在于加鎖的范圍,間隙鎖只鎖定兩個索引之間的引用間隙,而 next-key 鎖會鎖定多個索引區(qū)間,它包含「記錄鎖」和「間隙鎖」。所以,當我們使用了范圍查詢,不僅僅命中了已存在的 Record 記錄,還包含了 Gap 間隙。
總結
雖然在 InnoDB 引擎中通過間隙鎖和 next-key 鎖的方式解決了幻讀問題,但是加鎖之后會影響到數(shù)據(jù)庫的并發(fā)性能,因此,如果對性能要求較高的業(yè)務場景中,建議把隔離級別設置成 RC(READ COMMITTED),這個級別中不存在間隙鎖,但是需要考慮到幻讀問題會導致的數(shù)據(jù)一致性。
到此這篇關于InnoDB解決幻讀的方法詳解的文章就介紹到這了,更多相關InnoDB解決幻讀的方法內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
MySql狀態(tài)查看方法 MySql如何查看連接數(shù)和狀態(tài)?
如果是root帳號,你能看到所有用戶的當前連接。如果是其它普通帳號,只能看到自己占用的連接2012-11-11使用Python的Django框架中的壓縮組件Django Compressor
這篇文章主要介紹了使用Python的Django框架中的壓縮組件Django Compressor,這個工具主要用于實現(xiàn)js/css的壓縮,需要的朋友可以參考下2015-05-05MySQL數(shù)據(jù)庫開啟、關閉、查看函數(shù)功能的方法
這篇文章主要介紹了MySQL數(shù)據(jù)庫開啟、關閉、查看函數(shù)功能的方法,本文為解決一個錯誤總結而來,錯誤信息本文一同給出,需要的朋友可以參考下2014-10-10Mysql+Navicat16長期免費直連數(shù)據(jù)庫安裝使用超詳細教程
這篇文章主要介紹了Mysql+Navicat16長期免費直連數(shù)據(jù)庫安裝教程,這里下載的是mysql8版本,第一個安裝包比較小, 第二個安裝包比較大, 因為包含調(diào)試工具,我這里下載的是第一個,詳細介紹跟隨小編一起看看吧2023-11-11