騰訊云:深入理解MySQL中事務隔離級別的實現(xiàn)原理

來源: 騰訊云
作者:X先生
時間:2020-10-13
17230
本文就來聊聊MySQL中事務的隔離性的實現(xiàn)原理。

前言

說到數(shù)據(jù)庫事務,大家腦子里一定很容易蹦出一堆事務的相關知識,如事務的ACID特性,隔離級別,解決的問題(臟讀,不可重復讀,幻讀)等等,但是可能很少有人真正的清楚事務的這些特性又是怎么實現(xiàn)的,為什么要有四個隔離級別。

今天我們就先來聊聊MySQL中事務的隔離性的實現(xiàn)原理。

說明

MySQL的事務實現(xiàn)邏輯是位于引擎層的,并且不是所有的引擎都支持事務的,下面的說明都是以InnoDB引擎為基準。

定義

隔離性(isolation)指的是不同事務先后提交并執(zhí)行后,最終呈現(xiàn)出來的效果是串行的,也就是說,對于事務來說,它在執(zhí)行過程中,感知到的數(shù)據(jù)變化應該只有自己操作引起的,不存在其他事務引發(fā)的數(shù)據(jù)變化。

隔離性解決的是并發(fā)事務出現(xiàn)的問題。

標準SQL隔離級別

隔離性最簡單的實現(xiàn)方式就是各個事務都串行執(zhí)行了,如果前面的事務還沒有執(zhí)行完畢,后面的事務就都等待。但是這樣的實現(xiàn)方式很明顯并發(fā)效率不高,并不適合在實際環(huán)境中使用。

為了解決上述問題,實現(xiàn)不同程度的并發(fā)控制,SQL的標準制定者提出了不同的隔離級別:未提交讀(read uncommitted)、提交讀(read committed)、可重復讀(repeatable read)、序列化讀(serializable)。其中最高級隔離級別就是序列化讀,而在其他隔離級別中,由于事務是并發(fā)執(zhí)行的,所以或多或少允許出現(xiàn)一些問題。見以下的矩陣表:

1602557133(1).png

注意,MySQL的InnoDB引擎在提交讀級別通過MVCC解決了不可重復讀的問題,在可重復讀級別通過間隙鎖解決了幻讀問題,具體見下面的分析。

實現(xiàn)原理

標準SQL事務隔離級別實現(xiàn)原理

我們上面遇到的問題其實就是并發(fā)事務下的控制問題,解決并發(fā)事務的最常見方式就是悲觀并發(fā)控制了(也就是數(shù)據(jù)庫中的鎖)。標準SQL事務隔離級別的實現(xiàn)是依賴鎖的,我們來看下具體是怎么實現(xiàn)的:

1602557191(1).png

可以看到,在只使用鎖來實現(xiàn)隔離級別的控制的時候,需要頻繁的加鎖解鎖,而且很容易發(fā)生讀寫的沖突(例如在RC級別下,事務A更新了數(shù)據(jù)行1,事務B則在事務A提交前讀取數(shù)據(jù)行1都要等待事務A提交并釋放鎖)。

為了不加鎖解決讀寫沖突的問題,MySQL引入了MVCC機制。

InnoDB事務隔離級別實現(xiàn)原理

在往下分析之前,我們有幾個概念需要先了解下:

1、鎖定讀和一致性非鎖定讀

鎖定讀:在一個事務中,主動給讀加鎖,如SELECT...LOCK IN SHARE MODE和SELECT...FOR UPDATE。分別加上了行共享鎖和行排他鎖。

一致性非鎖定讀:InnoDB使用MVCC向事務的查詢提供某個時間點的數(shù)據(jù)庫快照。查詢會看到在該時間點之前提交的事務所做的更改,而不會看到稍后或未提交的事務所做的更改(本事務除外)。也就是說在開始了事務之后,事務看到的數(shù)據(jù)就都是事務開啟那一刻的數(shù)據(jù)了,其他事務的后續(xù)修改不會在本次事務中可見。

Consistent read是InnoDB在RC和RR隔離級別處理SELECT語句的默認模式。一致性非鎖定讀不會對其訪問的表設置任何鎖,因此,在對表執(zhí)行一致性非鎖定讀的同時,其它事務可以同時并發(fā)的讀取或者修改它們。

2、當前讀和快照讀

當前讀

讀取的是最新版本,像UPDATE、DELETE、INSERT、SELECT...LOCK IN SHARE MODE、SELECT...FOR UPDATE這些操作都是一種當前讀,為什么叫當前讀?就是它讀取的是記錄的最新版本,讀取時還要保證其他并發(fā)事務不能修改當前記錄,會對讀取的記錄進行加鎖。

快照讀

讀取的是快照版本,也就是歷史版本,像不加鎖的SELECT操作就是快照讀,即不加鎖的非阻塞讀;快照讀的前提是隔離級別不是未提交讀和序列化讀級別,因為未提交讀總是讀取最新的數(shù)據(jù)行,而不是符合當前事務版本的數(shù)據(jù)行,而序列化讀則會對表加鎖。

3、隱式鎖定和顯式鎖定

隱式鎖定

InnoDB在事務執(zhí)行過程中,使用兩階段鎖協(xié)議(不主動進行顯示鎖定的情況):

·隨時都可以執(zhí)行鎖定,InnoDB會根據(jù)隔離級別在需要的時候自動加鎖;

·鎖只有在執(zhí)行commit或者rollback的時候才會釋放,并且所有的鎖都是在同一時刻被釋放。

顯式鎖定

·InnoDB也支持通過特定的語句進行顯示鎖定(存儲引擎層)

select...lock in share mode//共享鎖

select...for update//排他鎖

·MySQL Server層的顯示鎖定:

lock table

unlock table

了解完上面的概念后,我們來看下InnoDB的事務具體是怎么實現(xiàn)的(下面的讀都指的是非主動加鎖的select)

1602557369(1).png

可以看到,InnoDB通過MVCC很好的解決了讀寫沖突的問題,而且提前一個級別就解決了標準級別下會出現(xiàn)的幻讀和不可重復讀問題,大大提升了數(shù)據(jù)庫的并發(fā)能力。

一些常見誤區(qū)

幻讀到底包不包括了delete的情況?

不可重復讀:前后多次讀取一行,數(shù)據(jù)內容不一致,針對其他事務的update和delete操作。為了解決這個問題,使用行共享鎖,鎖定到事務結束(也就是RR級別,當然MySQL使用MVCC在RC級別就解決了這個問題)

幻讀:當同一個查詢在不同時間生成不同的行集合時就是出現(xiàn)了幻讀,針對的是其他事務的insert操作,為了解決這個問題,鎖定整個表到事務結束(也就是S級別,當然MySQL使用間隙鎖在RR級別就解決了這個問題)

網(wǎng)上很多文章提到幻讀和提交讀的時候,有的說幻讀包括了delete的情況,有的說delete應該屬于提交讀的問題,那到底真相如何呢?我們實際來看下MySQL的官方文檔(如下)

The so-called phantom problem occurs within a transaction when the same query produces different sets of rows at different times.For example,if a SELECT is executed twice,but returns a row the second time that was not returned the first time,the row is a“phantom”row.

可以看到,幻讀針對的是結果集前后發(fā)生變化,所以看起來delete的情況應該歸為幻讀,但是我們實際分析下上面列出的標準SQL在RR級別的實現(xiàn)原理就知道,標準SQL的RR級別是會對查到的數(shù)據(jù)行加行共享鎖,所以這時候其他事務想刪除這些數(shù)據(jù)行其實是做不到的,所以在RR下,不會出現(xiàn)因delete而出現(xiàn)幻讀現(xiàn)象,也就是幻讀不包含delete的情況。

MVCC能解決了幻讀問題?

網(wǎng)上很多文章會說MVCC或者MVCC+間隙鎖解決了幻讀問題,實際上MVCC并不能解決幻讀問題。如以下的例子:

begin;

#假設users表為空,下面查出來的數(shù)據(jù)為空

select*from users;#沒有加鎖

#此時另一個事務提交了,且插入了一條id=1的數(shù)據(jù)

select*from users;#讀快照,查出來的數(shù)據(jù)為空

update users set name='mysql'where id=1;#update是當前讀,所以更新成功,并生成一個更新的快照

select*from users;#讀快照,查出來id為1的一條記錄,因為MVCC可以查到當前事務生成的快照

commit;

可以看到前后查出來的數(shù)據(jù)行不一致,發(fā)生了幻讀。所以說只有MVCC是不能解決幻讀問題的,解決幻讀問題靠的是間隙鎖。如下:

begin;

#假設users表為空,下面查出來的數(shù)據(jù)為空

select*from users lock in share mode;#加上共享鎖

#此時另一個事務B想提交且插入了一條id=1的數(shù)據(jù),由于有間隙鎖,所以要等待

select*from users;#讀快照,查出來的數(shù)據(jù)為空

update users set name='mysql'where id=1;#update是當前讀,由于不存在數(shù)據(jù),不進行更新

select*from users;#讀快照,查出來的數(shù)據(jù)為空

commit;

#事務B提交成功并插入數(shù)據(jù)

注意,RR級別下想解決幻讀問題,需要我們顯式加鎖,不然查詢的時候還是不會加鎖的。

立即登錄,閱讀全文
版權說明:
本文內容來自于騰訊云,本站不擁有所有權,不承擔相關法律責任。文章內容系作者個人觀點,不代表快出海對觀點贊同或支持。如有侵權,請聯(lián)系管理員(zzx@kchuhai.com)刪除!
優(yōu)質服務商推薦
更多
掃碼登錄
打開掃一掃, 關注公眾號后即可登錄/注冊
加載中
二維碼已失效 請重試
刷新
賬號登錄/注冊
個人VIP
小程序
快出海小程序
公眾號
快出海公眾號
商務合作
商務合作
投稿采訪
投稿采訪
出海管家
出海管家