最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

深入探究MySQL事務(wù)實(shí)現(xiàn)原理

 更新時(shí)間:2023年06月14日 10:53:25   作者:半畝方塘立身  
數(shù)據(jù)庫事務(wù)是指一組數(shù)據(jù)庫操作,這些操作必須被視為一個(gè)不可分割的單元,要么全部執(zhí)行成功,要么全部失敗回滾,本文詳細(xì)的給大家介紹了MySQL事務(wù)的實(shí)現(xiàn)原理,對我們學(xué)習(xí)MySQL有一定的幫助,感興趣的同學(xué)可以跟著小編一起來探究

什么是數(shù)據(jù)庫事務(wù)

數(shù)據(jù)庫事務(wù)是指一組數(shù)據(jù)庫操作,這些操作必須被視為一個(gè)不可分割的單元,要么全部執(zhí)行成功,要么全部失敗回滾。事務(wù)通常由多個(gè)SQL語句組成,這些語句可以讀取、插入、更新或刪除數(shù)據(jù)庫中的數(shù)據(jù)。
事務(wù)具有ACID屬性:

  • 原子性(Atomicity):事務(wù)的所有操作被視為單個(gè)原子操作,要么全部執(zhí)行成功,要么全部執(zhí)行失敗回滾。
  • 一致性(Consistency):事務(wù)執(zhí)行的結(jié)果必須使數(shù)據(jù)庫從一個(gè)一致性狀態(tài)轉(zhuǎn)換到另一個(gè)一致性狀態(tài),其中包括所有數(shù)據(jù)完整性和約束性規(guī)則的應(yīng)用。
  • 隔離性(Isolation):一個(gè)事務(wù)的執(zhí)行不能被其他并發(fā)執(zhí)行的事務(wù)干擾,每個(gè)事務(wù)應(yīng)該感覺自己在獨(dú)立地執(zhí)行。
  • 持久性(Durability):一旦事務(wù)提交,其結(jié)果應(yīng)該持久保存在數(shù)據(jù)庫中,即使系統(tǒng)故障也應(yīng)該如此。

通過實(shí)現(xiàn)事務(wù),數(shù)據(jù)庫系統(tǒng)可以確保數(shù)據(jù)的完整性和一致性,以及并發(fā)訪問時(shí)的正確性。如果一個(gè)事務(wù)中的任何一個(gè)操作失敗,整個(gè)事務(wù)將被回滾到最初的狀態(tài),這確保了數(shù)據(jù)庫的一致性。

Mysql如何保證原子性

undo log名為回滾日志,是實(shí)現(xiàn)原子性的關(guān)鍵。 InnoDB把這些為了回滾而記錄的這些東西稱之為undo log。這里需要注意的一點(diǎn)是,由于查詢操作(SELECT)并不會修改任何用戶記錄,所以在查詢操作執(zhí)行時(shí),并不需要記錄相應(yīng)的undo log。undo log主要分為3種:

  • Insert undo log :插入一條記錄時(shí),至少要把這條記錄的主鍵值記下來,之后回滾的時(shí)候只需要把這個(gè)主鍵值對應(yīng)的記錄刪掉就好了。

  • Update undo log:修改一條記錄時(shí),至少要把修改這條記錄前的舊值都記錄下來,這樣之后回滾時(shí)再把這條記錄更新為舊值就好了。

  • Delete undo log:刪除一條記錄時(shí),至少要把這條記錄中的內(nèi)容都記下來,這樣之后回滾時(shí)再把由這些內(nèi)容組成的記錄插入到表中就好了。

    • 刪除操作都只是設(shè)置一下老記錄的DELETED_BIT,并不真正將過時(shí)的記錄刪除。
    • 為了節(jié)省磁盤空間,InnoDB有專門的purge線程來清理DELETED_BIT為true的記錄。為了不影響MVCC的正常工作,purge線程自己也維護(hù)了一個(gè)read view(這個(gè)read view相當(dāng)于系統(tǒng)中最老活躍事務(wù)的read view);如果某個(gè)記錄的DELETED_BIT為true,并且DB_TRX_ID相對于purge線程的read view可見,那么這條記錄一定是可以被安全清除的。

舉個(gè)栗子:

sqlundo log
insertdelete
deleteinsert
update T set v=3 where v=1update T set v=1 where v=3

Mysql如何保證持久性

我們了解到InnoDB 為了提升讀寫效率,引入了Buffer Pool(緩存池):

  • 當(dāng)數(shù)據(jù)庫讀取數(shù)據(jù)時(shí),會首先從緩存池中讀取
  • 往數(shù)據(jù)庫寫入數(shù)據(jù)時(shí),會先寫入緩存池
  • 緩存池中更新的數(shù)據(jù)會定期刷新到磁盤中

如果MySQL宕機(jī),緩存池中更新的數(shù)據(jù)還沒有刷回到磁盤中,就會導(dǎo)致數(shù)據(jù)丟失。于是,redo log被引入進(jìn)來解決這個(gè)問題。

  • 先將原始數(shù)據(jù)從磁盤中讀入內(nèi)存中來,修改數(shù)據(jù)的內(nèi)存拷貝。
  • 生成一條重做日志并寫入redo log buffer,記錄的是數(shù)據(jù)被修改后的值。
  • 當(dāng)事務(wù)commit時(shí),將redo log buffer中的內(nèi)容刷新到 redolog file,對 redo log file采用追加寫的方式。
  • 定期將內(nèi)存中修改的數(shù)據(jù)刷新到磁盤中。

redoundo在一次事務(wù)操作中是如何交互的?假設(shè)有A、B兩個(gè)數(shù)據(jù),值分別為1、2,開啟事務(wù)分別對其進(jìn)行修改A → 3,B → 4,在提交,過程如下:

事務(wù)redo&undo logo
begin;開啟事務(wù)
記錄A->3到redo log buffer
update T set A=3 where A=1;A修改為3
記錄A=1到undo log
記錄B->4到redo log buffer
update T set B=4 where B=2;B修改為4
記錄B=2到undo log
記錄A->3到redo log記錄B->4到redo log
commit;事務(wù)提交

MySQL怎么保證隔離性

事務(wù)在并發(fā)情形下會互相干擾到的操作大體可以分為兩類,與之相對應(yīng)地,MySQL采用了兩種方式來實(shí)現(xiàn)它們的隔離:

  • 一個(gè)事務(wù)的寫操作對另一個(gè)事務(wù)的寫操作的影響:鎖機(jī)制保證隔離性
  • 一個(gè)事務(wù)的寫操作對另一個(gè)事務(wù)的讀操作的影響:MVCC保證隔離性

加鎖:讀取數(shù)據(jù)之前,對其加鎖,阻止其他事務(wù)對數(shù)據(jù)進(jìn)行修改

MVCC:不加任何鎖,采用多版本并發(fā)控制實(shí)現(xiàn),把數(shù)據(jù)庫的行鎖和行的多個(gè)版本結(jié)合起來,可以實(shí)現(xiàn)非鎖定讀,從而提高數(shù)據(jù)庫的并發(fā)性能。

事務(wù)隔離級別

當(dāng)數(shù)據(jù)庫上有多個(gè)事務(wù)同時(shí)執(zhí)行的時(shí)候,會帶來以下問題:

問題描述舉例
臟讀一個(gè)事務(wù)讀到了另一個(gè)事務(wù)未提交修改的數(shù)據(jù)。事務(wù)A開始一個(gè)更新操作,但是還沒有提交,這時(shí)事務(wù)B讀取了這個(gè)未提交的數(shù)據(jù),就會產(chǎn)生臟讀。
幻讀一個(gè)事務(wù)按相同的查詢條件重新讀取以前檢索過的數(shù)據(jù),卻發(fā)現(xiàn)其他事務(wù)插入了滿足其查詢條件的新數(shù)據(jù)。事務(wù)A進(jìn)行一個(gè)范圍查詢,此時(shí)事務(wù)B插入了一些符合該范圍查詢條件的新數(shù)據(jù),當(dāng)事務(wù)A再次進(jìn)行相同的范圍查詢時(shí),會發(fā)現(xiàn)多了一些之前沒有的行,就產(chǎn)生了幻讀。
不可重復(fù)讀在一個(gè)事務(wù)中,多次查詢的數(shù)據(jù)不一致。事務(wù)A讀取了一行數(shù)據(jù),然后事務(wù)B對這一行數(shù)據(jù)進(jìn)行了更新,并且提交了,當(dāng)事務(wù)A再次讀取這一行數(shù)據(jù)時(shí),會發(fā)現(xiàn)數(shù)據(jù)已經(jīng)發(fā)生了變化,就產(chǎn)生了不可重復(fù)讀。

為了避免這些問題的出現(xiàn),數(shù)據(jù)庫引入了隔離級別的概念,通過對不同隔離級別的設(shè)置,可以控制事務(wù)之間的隔離程度,從而避免并發(fā)問題的產(chǎn)生。不同的隔離級別有不同的特點(diǎn)和使用場景,需要根據(jù)實(shí)際情況進(jìn)行選擇。

以下是四個(gè)標(biāo)準(zhǔn)的事務(wù)隔離級別:

隔離級別含義臟讀不可重復(fù)讀幻讀
讀未提交,Read Uncommitted事務(wù)中的修改,即使沒有提交,對其他事務(wù)都是可見的YYY
讀已提交,Read Committed事務(wù)從開始到提交之前,所做的修改對其他事務(wù)都不可見NYY
可重復(fù)讀,Repeatable read同一事務(wù)中多次讀取同樣的記錄結(jié)果是一致的NNY
可序列化,Serializable在讀取的每一行數(shù)據(jù)上加鎖,強(qiáng)制事務(wù)串行執(zhí)行NNN

臟讀的解決

Innodb是通過在每行數(shù)據(jù)中增加一個(gè)隱藏的事務(wù)ID來實(shí)現(xiàn)mvcc,當(dāng)一個(gè)事物開始時(shí)他會獲取一個(gè)唯一的事務(wù)ID,該事務(wù)ID用來標(biāo)記事務(wù)做的修改。當(dāng)事務(wù)讀取一行數(shù)據(jù)時(shí),innodb會檢查該行數(shù)據(jù)事務(wù)ID是否小于當(dāng)前事務(wù)ID,如果是說明該行數(shù)據(jù)是未提交的數(shù)據(jù),innodb會阻止該事務(wù)讀取該行數(shù)據(jù),從而避免了臟讀的問題。

不可重復(fù)讀的解決

innodb通過mvcc解決不可重復(fù)讀的問題,在RR數(shù)據(jù)庫隔離級別下,當(dāng)我們使用快照進(jìn)行數(shù)據(jù)讀取的時(shí)候,只會在第一次讀取的時(shí)候生成一個(gè)ReadView,后續(xù)所有快照讀都是使用同一個(gè)快照,所以就不會發(fā)生不可重復(fù)讀的問題了。

可重復(fù)讀模式下舉個(gè)栗子: 事務(wù)隔離級別為RR:

創(chuàng)建個(gè)測試表,并插入一條數(shù)據(jù)(1,1,1)

create table table1(
    id int(11) not null,
? ? a varchar(50) default null,
? ? b varchar(50) default null,
? ? primary key(id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

分別開啟兩個(gè)事務(wù)測試:

幻讀的解決

innodb的mvcc和間隙鎖在一定程度上避免了幻讀的發(fā)生,但是沒有辦法完全避免,當(dāng)一個(gè)事務(wù)讀的時(shí)候會導(dǎo)致幻讀的發(fā)生。

幻讀的case

  • 創(chuàng)建一個(gè)用戶表

create table user(
    id int not null,
    name varchar(50),
    age int,
    primary key(id)
);
  • 插入幾條數(shù)據(jù)
insert into user values(1,'張三',10),(2,'李四',20),(3,'王二',30);

  • 分別開啟兩個(gè)事務(wù)測試:

MVCC實(shí)現(xiàn)

每條記錄在更新的時(shí)候都會同時(shí)記錄一條回滾操作。同一條記錄在系統(tǒng)中可以存在多個(gè)版本,這就是數(shù)據(jù)庫的多版本并發(fā)控制(MVCC)。

MySQL中每條記錄,除了我們自定義的字段之外,還有數(shù)據(jù)庫隱藏定義的三個(gè)字段:

字段描述
DB_TRX_ID6字節(jié),最近修改事務(wù)id,記錄創(chuàng)建這套記錄后者最后一次修改該記錄的事務(wù)id
DB_ROLL_PTR7字節(jié),回滾指針,指向這條記錄的上一個(gè)版本,用于配合undolog
DB_ROW_ID6字節(jié),隱藏的主鍵,如果數(shù)據(jù)表沒有主鍵,那么innodb會生成一個(gè)6字節(jié)的row_id

在 MySQL 中,實(shí)際上每條記錄在更新的時(shí)候都會同時(shí)記錄一條回滾操作。記錄上的最新值,通過回滾操作,都可以得到前一個(gè)狀態(tài)的值。

InnoDB 并不會真正地去開辟空間存儲多個(gè)版本的行記錄,只是借助 undo log 記錄每次寫操作的反向操作。所以B+ 索引樹上對應(yīng)的記錄只會有一個(gè)最新版本,InnoDB 可以根據(jù) undo log 得到數(shù)據(jù)的歷史版本,從而實(shí)現(xiàn)多版本控制。

Read View

什么是Read View,說白了Read View就是事務(wù)進(jìn)行快照讀操作的時(shí)候生產(chǎn)的讀視圖(Read View),在該事務(wù)執(zhí)行的快照讀的那一刻,會生成數(shù)據(jù)庫系統(tǒng)當(dāng)前的一個(gè)快照,記錄并維護(hù)系統(tǒng)當(dāng)前活躍事務(wù)的ID(當(dāng)每個(gè)事務(wù)開啟時(shí),都會被分配一個(gè)ID, 這個(gè)ID是遞增的,所以最新的事務(wù),ID值越大)

所以我們知道 Read View主要是用來做可見性判斷的, 即當(dāng)我們某個(gè)事務(wù)執(zhí)行快照讀的時(shí)候,對該記錄創(chuàng)建一個(gè)Read View讀視圖,把它比作條件用來判斷當(dāng)前事務(wù)能夠看到哪個(gè)版本的數(shù)據(jù),即可能是當(dāng)前最新的數(shù)據(jù),也有可能是該行記錄的undo log里面的某個(gè)版本的數(shù)據(jù)。

Read View遵循一個(gè)可見性算法,主要是將要被修改的數(shù)據(jù)的最新記錄中的DB_TRX_ID(即當(dāng)前事務(wù)ID)取出來,與系統(tǒng)當(dāng)前其他活躍事務(wù)的ID去對比(由Read View維護(hù)),如果DB_TRX_ID跟Read View的屬性做了某些比較,不符合可見性,那就通過DB_ROLL_PTR回滾指針去取出Undo Log中的DB_TRX_ID再比較,即遍歷鏈表的DB_TRX_ID(從鏈?zhǔn)椎芥溛?,即從最近的一次修改查起),直到找到滿足特定條件的DB_TRX_ID, 那么這個(gè)DB_TRX_ID所在的舊記錄就是當(dāng)前事務(wù)能看見的最新老版本

假設(shè)一個(gè)值從 1 被按順序改成了 2、3、4,在回滾日志里面就會有類似下面的記錄。

當(dāng)前值是 4,但是在查詢這條記錄的時(shí)候,不同時(shí)刻啟動(dòng)的事務(wù)會有不同的 read-view。如圖中看到的,在視圖 A、B、C 里面,這一個(gè)記錄的值分別是 1、2、4,同一條記錄在系統(tǒng)中可以存在多個(gè)版本,就是數(shù)據(jù)庫的多版本并發(fā)控制(MVCC)。對于 read-view A,要得到 1,就必須將當(dāng)前值依次執(zhí)行圖中所有的回滾操作得到。同時(shí)你會發(fā)現(xiàn),即使現(xiàn)在有另外一個(gè)事務(wù)正在將 4 改成 5,這個(gè)事務(wù)跟 read-view A、B、C 對應(yīng)的事務(wù)是不會沖突的。你一定會問,回滾日志總不能一直保留吧,什么時(shí)候刪除呢?答案是,在不需要的時(shí)候才刪除。也就是說,系統(tǒng)會判斷,當(dāng)沒有事務(wù)再需要用到這些回滾日志時(shí),回滾日志會被刪除。什么時(shí)候才不需要了呢?就是當(dāng)系統(tǒng)里沒有比這個(gè)回滾日志更早的 read-view 的時(shí)候。

RC、RR級別下的InnoDB快照讀有什么不同?

在可重復(fù)讀隔離級別下,只需要在事務(wù)開始的時(shí)候創(chuàng)建一致性視圖,之后事務(wù)里的其他查詢都共用這個(gè)一致性視圖;

在讀提交隔離級別下,每一個(gè)語句執(zhí)行前都會重新算出一個(gè)新的視圖。

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

相關(guān)文章

  • MySQL8.0無法遠(yuǎn)程連接訪問的解決方法

    MySQL8.0無法遠(yuǎn)程連接訪問的解決方法

    本文主要介紹了MySQL8.0無法遠(yuǎn)程連接訪問的解決方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-01-01
  • MySQL因大事務(wù)導(dǎo)致的Insert慢實(shí)例分析

    MySQL因大事務(wù)導(dǎo)致的Insert慢實(shí)例分析

    這篇文章主要給大家介紹了關(guān)于MySQL因大事務(wù)導(dǎo)致Insert慢的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-10-10
  • Mysql行轉(zhuǎn)列把逗號分隔的字段拆分成多行兩種方法

    Mysql行轉(zhuǎn)列把逗號分隔的字段拆分成多行兩種方法

    在數(shù)據(jù)庫中有時(shí)候我們需要將一些行數(shù)據(jù)轉(zhuǎn)換為列數(shù)據(jù),這在處理數(shù)據(jù)透視表、報(bào)表生成等場景下非常常見,這篇文章主要給大家介紹了關(guān)于Mysql行轉(zhuǎn)列把逗號分隔的字段拆分成多行的兩種方法,需要的朋友可以參考下
    2024-05-05
  • mysql中截取字符串的6個(gè)函數(shù)講解

    mysql中截取字符串的6個(gè)函數(shù)講解

    這篇文章主要介紹了mysql中截取字符串的6個(gè)函數(shù)講解,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • ERROR 1045 (28000): Access denied for user ''''root''''@''''localhost'''' (using password: YES)實(shí)用解決方案

    ERROR 1045 (28000): Access denied for user ''''root''''@''''

    這篇文章主要介紹了Linux下數(shù)據(jù)庫報(bào)ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES)實(shí)用解決方案,希望能對大家有幫助。
    2017-10-10
  • mysql 5.7.24 安裝配置圖文教程

    mysql 5.7.24 安裝配置圖文教程

    這篇文章主要為大家詳細(xì)介紹了mysql 5.7.24 安裝配置圖文教程,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-11-11
  • MySQL binlog 遠(yuǎn)程備份方法詳解

    MySQL binlog 遠(yuǎn)程備份方法詳解

    以前備份binlog時(shí),都是先在本地進(jìn)行備份壓縮,然后發(fā)送到遠(yuǎn)程服務(wù)器中,但是這種備份方式非常的不安全,下面通過本文給大家介紹MySQL binlog 遠(yuǎn)程備份的方法,需要的朋友一起學(xué)習(xí)吧
    2016-05-05
  • Mysql的游標(biāo)的定義使用及關(guān)閉深入分析

    Mysql的游標(biāo)的定義使用及關(guān)閉深入分析

    于游標(biāo)的用法Mysql現(xiàn)在提供的還很特別,雖然使用起來沒有PL/SQL那么順手,不過使用上大致上還是一樣,本文將詳細(xì)介紹一下,需要了解的朋友可以參考下
    2012-12-12
  • 教你如何在windows與linux系統(tǒng)中設(shè)置MySQL數(shù)據(jù)庫名、表名大小寫敏感

    教你如何在windows與linux系統(tǒng)中設(shè)置MySQL數(shù)據(jù)庫名、表名大小寫敏感

    數(shù)據(jù)庫和表名在 Windows 中是大小寫不敏感的,而在大多數(shù)類型的 Unix/Linux 系統(tǒng)中是大小寫敏感的。那么我們?nèi)绾蝸硖幚磉@個(gè)問題呢,經(jīng)過一番查詢,發(fā)現(xiàn)lower_case_table_names這個(gè)參數(shù)可以實(shí)現(xiàn)大小寫敏感,下面我們來詳細(xì)說明
    2014-08-08
  • Windows10下mysql 8.0.12解壓版安裝配置方法圖文教程

    Windows10下mysql 8.0.12解壓版安裝配置方法圖文教程

    這篇文章主要為大家詳細(xì)介紹了Windows10下mysql 8.0.12解壓版安裝方法圖文教程,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-08-08

最新評論

布拖县| 得荣县| 平江县| 池州市| 陵水| 芒康县| 宜阳县| 贵南县| 陇南市| 日喀则市| 龙南县| 砚山县| 老河口市| 渭南市| 安义县| 四子王旗| 永宁县| 香港 | 太白县| 金沙县| 柳州市| 乌兰浩特市| 东港市| 富平县| 菏泽市| 西城区| 达拉特旗| 安义县| 神池县| 凌云县| 苍山县| 哈巴河县| 昌图县| 榕江县| 额敏县| 北碚区| 新竹市| 茶陵县| 襄垣县| 永吉县| 古丈县|