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

詳解MySQL中事務(wù)隔離級別的實(shí)現(xiàn)原理

 更新時(shí)間:2021年01月28日 14:59:05   作者:X先生  
這篇文章主要介紹了MySQL中事務(wù)隔離級別的實(shí)現(xiàn)原理,幫助大家更好的理解和使用MySQL數(shù)據(jù)庫,感興趣的朋友可以了解下

前言

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

今天我們就先來聊聊MySQL中事務(wù)的隔離性的實(shí)現(xiàn)原理,后續(xù)還會繼續(xù)出文章分析其他特性的實(shí)現(xiàn)原理。

當(dāng)然MySQL博大精深,文章疏漏之處在所難免,歡迎批評指正。

說明

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

定義

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

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

標(biāo)準(zhǔn)SQL隔離級別

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

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

隔離級別(+:允許出現(xiàn),-:不允許出現(xiàn)) 臟讀 不可重復(fù)讀 幻讀
未提交讀                                  +         +               +        
提交讀                                    -         +               +        
可重復(fù)讀                                  -         -               +        
序列化讀                                  -         -               -        

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

實(shí)現(xiàn)原理

標(biāo)準(zhǔn)SQL事務(wù)隔離級別實(shí)現(xiàn)原理

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

事務(wù)隔離級別    實(shí)現(xiàn)方式                                                     
未提交讀(RU) 事務(wù)對當(dāng)前被讀取的數(shù)據(jù)不加鎖;

事務(wù)在更新某數(shù)據(jù)的瞬間(就是發(fā)生更新的瞬間),必須先對其加行級共享鎖,直到事務(wù)結(jié)束才釋放。
提交讀(RC)    事務(wù)對當(dāng)前被讀取的數(shù)據(jù)加行級共享鎖(當(dāng)讀到時(shí)才加鎖),一旦讀完該行,立即釋放該行級共享鎖;

事務(wù)在更新某數(shù)據(jù)的瞬間(就是發(fā)生更新的瞬間),必須先對其加行級排他鎖,直到事務(wù)結(jié)束才釋放。
可重復(fù)讀(RR) 事務(wù)在讀取某數(shù)據(jù)的瞬間(就是開始讀取的瞬間),必須先對其加行級共享鎖,直到事務(wù)結(jié)束才釋放;

事務(wù)在更新某數(shù)據(jù)的瞬間(就是發(fā)生更新的瞬間),必須先對其加行級排他鎖,直到事務(wù)結(jié)束才釋放。
序列化讀(S)   事務(wù)在讀取數(shù)據(jù)時(shí),必須先對其加表級共享鎖 ,直到事務(wù)結(jié)束才釋放;

事務(wù)在更新數(shù)據(jù)時(shí),必須先對其加表級排他鎖 ,直到事務(wù)結(jié)束才釋放。

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

為了不加鎖解決讀寫沖突的問題,MySQL引入了MVCC機(jī)制,詳細(xì)可見我以前的分析文章:一文讀懂?dāng)?shù)據(jù)庫中的樂觀鎖和悲觀鎖和MVCC。

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

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

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

鎖定讀:在一個(gè)事務(wù)中,主動(dòng)給讀加鎖,如SELECT ... LOCK IN SHARE MODE 和 SELECT ... FOR UPDATE。分別加上了行共享鎖和行排他鎖。鎖的分類可見我以前的分析文章:你應(yīng)該了解的MySQL鎖分類)。

https://dev.mysql.com/doc/refman/8.0/en/innodb-locking-reads.html

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

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

https://dev.mysql.com/doc/refman/8.0/en/innodb-consistent-read.html

2、當(dāng)前讀和快照讀

當(dāng)前讀

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

快照讀

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

3、隱式鎖定和顯式鎖定

隱式鎖定

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

  • 隨時(shí)都可以執(zhí)行鎖定,InnoDB會根據(jù)隔離級別在需要的時(shí)候自動(dòng)加鎖;
  • 鎖只有在執(zhí)行commit或者rollback的時(shí)候才會釋放,并且所有的鎖都是在同一時(shí)刻被釋放。

顯式鎖定

  • InnoDB也支持通過特定的語句進(jìn)行顯示鎖定(存儲引擎層)
select ... lock in share mode //共享鎖
select ... for update //排他鎖
  • MySQL Server層的顯示鎖定:
lock table
unlock table

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

事務(wù)隔離級別    實(shí)現(xiàn)方式                                                     
未提交讀(RU) 事務(wù)對當(dāng)前被讀取的數(shù)據(jù)不加鎖,都是當(dāng)前讀;

事務(wù)在更新某數(shù)據(jù)的瞬間(就是發(fā)生更新的瞬間),必須先對其加行級共享鎖,直到事務(wù)結(jié)束才釋放。
提交讀(RC)    事務(wù)對當(dāng)前被讀取的數(shù)據(jù)不加鎖,且是快照讀;

事務(wù)在更新某數(shù)據(jù)的瞬間(就是發(fā)生更新的瞬間),必須先對其加行級排他鎖(Record),直到事務(wù)結(jié)束才釋放。

通過快照,在這個(gè)級別MySQL就解決了不可重復(fù)讀的問題
可重復(fù)讀(RR) 事務(wù)對當(dāng)前被讀取的數(shù)據(jù)不加鎖,且是快照讀;

事務(wù)在更新某數(shù)據(jù)的瞬間(就是發(fā)生更新的瞬間),必須先對其加行級排他鎖(Record,GAP,Next-Key),直到事務(wù)結(jié)束才釋放。

通過間隙鎖,在這個(gè)級別MySQL就解決了幻讀的問題
序列化讀(S)   事務(wù)在讀取數(shù)據(jù)時(shí),必須先對其加表級共享鎖 ,直到事務(wù)結(jié)束才釋放,都是當(dāng)前讀;

事務(wù)在更新數(shù)據(jù)時(shí),必須先對其加表級排他鎖 ,直到事務(wù)結(jié)束才釋放。

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

一些常見誤區(qū)

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

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

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

網(wǎng)上很多文章提到幻讀和提交讀的時(shí)候,有的說幻讀包括了delete的情況,有的說delete應(yīng)該屬于提交讀的問題,那到底真相如何呢?我們實(shí)際來看下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.
https://dev.mysql.com/doc/refman/5.7/en/innodb-next-key-locking.html

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

MVCC能解決了幻讀問題?

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

begin;

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

select * from users; #沒有加鎖

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

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

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

select * from users; #讀快照,查出來id為1的一條記錄,因?yàn)镸VCC可以查到當(dāng)前事務(wù)生成的快照

commit;

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

begin;

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

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

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

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

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

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

commit;

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

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

以上就是詳解MySQL中事務(wù)隔離級別的實(shí)現(xiàn)原理的詳細(xì)內(nèi)容,更多關(guān)于MySQL 事務(wù)隔離級別的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • MySQL數(shù)據(jù)庫的高可用方案總結(jié)

    MySQL數(shù)據(jù)庫的高可用方案總結(jié)

    這篇文章主要針對MySQL數(shù)據(jù)庫的高可用方案進(jìn)行詳細(xì)總結(jié),高可用架構(gòu)對于互聯(lián)網(wǎng)服務(wù)基本是標(biāo),本文是對各種方案的總結(jié),感興趣的小伙伴們可以參考一下
    2016-05-05
  • 詳解如何修改MySQL最大連接數(shù)

    詳解如何修改MySQL最大連接數(shù)

    本文主要介紹了詳解如何修改MySQL最大連接數(shù),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-08-08
  • navicat?連接Ubuntu虛擬機(jī)的mysql的操作方法

    navicat?連接Ubuntu虛擬機(jī)的mysql的操作方法

    這篇文章主要介紹了navicat?連接Ubuntu虛擬機(jī)的mysql的相關(guān)知識,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-04-04
  • mysql學(xué)習(xí)筆記之基礎(chǔ)知識

    mysql學(xué)習(xí)筆記之基礎(chǔ)知識

    本文是MySQL學(xué)習(xí)筆記系列文章的第一篇,給大家簡單講解下MySQL的一些檢查操作命令,希望大家能夠喜歡
    2017-02-02
  • mysql 悲觀鎖與樂觀鎖的理解及應(yīng)用分析

    mysql 悲觀鎖與樂觀鎖的理解及應(yīng)用分析

    這篇文章主要介紹了mysql 悲觀鎖與樂觀鎖的理解及應(yīng)用,結(jié)合實(shí)例形式分析了MySQL數(shù)據(jù)庫悲觀鎖與樂觀鎖相關(guān)概念、原理、使用方法及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下
    2020-02-02
  • MYSQL大表改字段慢問題的解決

    MYSQL大表改字段慢問題的解決

    本文主要介紹了MYSQL大表改字段慢問題的解決,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-03-03
  • MySQL在哪些情況下不使用索引的示例

    MySQL在哪些情況下不使用索引的示例

    盡管索引可以顯著提高數(shù)據(jù)庫的查詢性能,但在某些情況下,MySQL可能不會使用索引,本文就來介紹一下MySQL在哪些情況下不使用索引,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-01-01
  • MYSQL替換時(shí)間(年月日)字段時(shí)分秒不變實(shí)例解析

    MYSQL替換時(shí)間(年月日)字段時(shí)分秒不變實(shí)例解析

    這篇文章主要介紹了MYSQL替換時(shí)間(年月日)字段,時(shí)分秒不變的實(shí)現(xiàn)方法,需要的朋友可以參考下
    2017-07-07
  • MySQL從命令行導(dǎo)入SQL腳本時(shí)出現(xiàn)中文亂碼的解決方法

    MySQL從命令行導(dǎo)入SQL腳本時(shí)出現(xiàn)中文亂碼的解決方法

    這篇文章主要介紹了MySQL從命令行導(dǎo)入SQL腳本時(shí)出現(xiàn)中文亂碼的解決方法,分析了中文亂碼出現(xiàn)的原因并給出了兩種解決方法供大家參考,需要的朋友可以參考下
    2016-09-09
  • mysql備份表的幾種方法總結(jié)

    mysql備份表的幾種方法總結(jié)

    這篇文章主要介紹了mysql的備份表的幾種方法總結(jié),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-03-03

最新評論