日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

MySQL事务——万字详解

發(fā)布時間:2024/3/12 数据库 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 MySQL事务——万字详解 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

目錄

前言

一.事務(wù)的概念

? ? ? ? 1.1 什么是事務(wù)

? ? ? ? 1.2 事務(wù)的屬性

? ? ? ? 1.3 事務(wù)的版本支持

? ? ? ? ?1.4 事務(wù)提交方式

? ? ? ? ?1.5 事務(wù)的常見操作

? ? ? ? 1.5.1 準(zhǔn)備階段:

? ? ? ? ?1.5.2 手動演示回滾操作

? ? ? ? ? 1.5.3 簡單證明原子性

? ? ? ? ?1.5.4 簡單證明持久性? ? ?

? ? ? ? 1.5.5 begin開啟的事務(wù)不會受MySQL事務(wù)提交方式的影響

? ? ? ? ?1.5.6 MySQL中SQL與事務(wù)的關(guān)系

? ? ? ? ?1.5.7 總結(jié)

?二.隔離級別

? ? ? ? 2.1 隔離級別介紹

? ? ? ? 2.2 查看和設(shè)置隔離性

? ? ? ? 2.3 操作演示

? ? ? ? 2.3.1 讀未提交

? ? ? ? 2.3.2 讀提交

? ? ? ? ?2.3.3 可重復(fù)讀

? ? ? ? ?2.3.4 串行化

????????2.3.5 總結(jié)

? ? ? ? 2.4 一致性

三.解決并發(fā)的原理MVCC

? ? ? ? 3.1 數(shù)據(jù)庫并發(fā)的三種場景

? ? ? ? 3.2 讀和寫

? ? ? ? 3.2.1 三個隱藏字段

? ? ? ? 3.2.2 undo日志

? ? ? ? ?3.2.3 模擬MVCC過程

? ? ? ? 3.2.4 read view

?四.RR和RC的本質(zhì)區(qū)別


前言

? ? ? ? MySQL是一個網(wǎng)絡(luò)服務(wù)。大多數(shù)情況下,會有很多客戶端連接MySQL服務(wù)。當(dāng)多個客戶端訪問同一個表時,可能會出現(xiàn)問題。

? ? ? ? 比如:火車票售票系統(tǒng),當(dāng)兩個客戶端同時買票,操作同一張票數(shù)表。當(dāng)客戶端A檢測還有一張票,將票買掉,但是還沒有更新數(shù)據(jù)庫。于此同時,客戶端B,也在買票,也檢測到還有一張票,客戶端B也將票買了。這樣就導(dǎo)致一張票被賣了兩次。

? ? ? ? 于是MySQL需要對此現(xiàn)象加以控制。這就是事務(wù)解決的問題。

要解決上面的問題,至少需要滿足下面的屬性(拿買票的過程舉例):

  • 買票的過程得是原子的
  • 多個客戶端買票,互相之間不能影響
  • 買完票后,數(shù)據(jù)應(yīng)該是永久有效的
  • 買票前各個客戶端之間看到的票數(shù)要是一樣的;買票后,各個客戶端看到的票數(shù)是一樣的

一.事務(wù)的概念

? ? ? ? 1.1 什么是事務(wù)

? ? ? ? 事務(wù)就是一組DML類的SQL語句,這些語句在邏輯上存在相關(guān)性,這一組DML語句要么全部成功,要么全部失敗,是一個整體。

? ? ? ? 簡單來說,事務(wù)就是要完成一件事,所用到的所有SQL語句(至少一條)。這些語句要么全部執(zhí)行成功,要么全部執(zhí)行失敗。即,其中一條執(zhí)行失敗,就全部執(zhí)行失敗。數(shù)據(jù)立馬回滾到執(zhí)行前的狀態(tài)。

? ? ? ? 1.2 事務(wù)的屬性

  • 原子性:一個事務(wù)中的所有操作,要么全部完成,要么全部不完成。不會結(jié)束在中間的某個環(huán)節(jié)。事務(wù)在執(zhí)行過程中發(fā)生錯誤,會被回滾到事務(wù)開始前的狀態(tài),就像這個事務(wù)沒有發(fā)生一樣。
  • 持久性:事務(wù)處理結(jié)束后,對數(shù)據(jù)的修改就是永久的,即便系統(tǒng)故障也不會丟失。可以理解成,事務(wù)執(zhí)行完后,就將數(shù)據(jù)刷新到磁盤中了。但是,這并沒有這么簡單,因為,MySQL是是一個應(yīng)用程序,數(shù)據(jù)刷新需要操作系統(tǒng)來做。故MySQL需要先將數(shù)據(jù)放到內(nèi)核緩沖區(qū)中,再由操作系統(tǒng)將數(shù)據(jù)刷新到磁盤。
  • 隔離性:數(shù)據(jù)庫允許多個事務(wù)并發(fā)運行,并且允許事務(wù)同時對數(shù)據(jù)進行讀,寫和修改。隔離性可以防止多個事務(wù)并發(fā)執(zhí)行時由于交叉執(zhí)行而導(dǎo)致數(shù)據(jù)不一致的情況。但是,MySQL為了考慮效率,多個事務(wù)讀,寫或者修改同一數(shù)據(jù)時,并不一定是串行運行的。而是分成了不同的級別:讀未提交(read? uncommitted),讀提交(read committed),可重復(fù)讀(repeatable read)和串行化(serializable)。
  • 一致性:在事務(wù)開始前和事務(wù)結(jié)束后,數(shù)據(jù)庫的完整性。并且事務(wù)執(zhí)行后,會完全符合結(jié)果。這需要MySQL和用戶共同來保證的。即,MySQL保證了數(shù)據(jù)保護出錯,用戶需要保存流程正確。

上面四個屬性,可以簡稱ACID。

  • 原子性(Atomicity,又稱不可分割性)
  • 一致性(Consistency)
  • 隔離性(Isolation,又稱獨立性)
  • 持久性(Durability)

事務(wù)并不是伴隨著數(shù)據(jù)庫系統(tǒng)天生就有的,而是為應(yīng)用層的服務(wù)的。

這樣就使得用戶在操作數(shù)據(jù)庫時不需要考慮數(shù)據(jù)的安全問題,簡化了編程模型。

? ? ? ? 1.3 事務(wù)的版本支持

? ? ? ? 事務(wù)是存儲引擎提供的,在MySQL中只有使用了innodb存儲引擎的數(shù)據(jù)庫或者表才支持事務(wù),MyISAM存儲引擎不支持事務(wù)。

查看數(shù)據(jù)庫存儲引擎:

? ? ? ? ?1.4 事務(wù)提交方式

? ? ? ? 事務(wù)提交方式有兩種:

  • 手動提交
  • 自動提交

查看事務(wù)的提交方式:

?修改事務(wù)的提交方式:

? ? ? ? ?1.5 事務(wù)的常見操作

? ? ? ? 1.5.1 準(zhǔn)備階段:

手動啟動一個事務(wù):

begin/start transaction;--開始一個事務(wù)...--對表進行操作commit;--提交事務(wù)

?設(shè)置隔離級別為讀未提交,后面在隔離性有詳細(xì)解釋。

注意:設(shè)置完global.transaction_isolation,需要重啟MySQL。

?創(chuàng)建表:

create table if not exists account(id int primary key,name varchar(50) not null default '',blance decimal(10,2) not null default 0.0)ENGINE=InnoDB DEFAULT CHARSET=UTF8;

? ? ? ? ?1.5.2 手動演示回滾操作

? ? ? ? 創(chuàng)建保存點:savepoint? 保存點名;

? ? ? ? 回滾:rollback? 回滾的保存點;

? ? ? ? ? 1.5.3 簡單證明原子性

? ? ? ? 注意:現(xiàn)在的隔離等級是讀為提交。

? ? ? ? 事務(wù)沒有執(zhí)行完發(fā)生了錯誤,會回滾到最開始,相當(dāng)于這個事務(wù)沒有發(fā)生一樣。

? ? ? ? ?1.5.4 簡單證明持久性? ? ?

? ? ? ? ? 當(dāng)一個事務(wù)完成后,操作完的數(shù)據(jù)會永久保存,系統(tǒng)是否故障。

? ? ? ? 1.5.5 begin開啟的事務(wù)不會受MySQL事務(wù)提交方式的影響

? ? ? ? 結(jié)論:begin開啟的事務(wù),不受MySQL事務(wù)提交方式的影響,必須手動commit提交。

? ? ? ? ?1.5.6 MySQL中SQL與事務(wù)的關(guān)系

? ? ? ? 結(jié)論:在MySQL中沒有手動begin開啟事務(wù),增刪查改都會被MySQL封裝成一個事務(wù),即使只有一條SQL語句。

提交方式為:自動提交。

?提交方式為:手動提交

?正確:

? ? ? ? ?1.5.7 總結(jié)

  • 事務(wù)可以手動回滾,同時,操作異常會自動回滾。
  • 使用begin/start? transaction開啟的事務(wù),必須通過commit才能提交,才會持久化,與MySQL的提交方式無關(guān)。
  • 對于MySQL存儲引擎使用innodb,每一條SQL語句都會默認(rèn)封裝成事務(wù),提交方式看auto_commit是否開啟。

注意點:

  • 如果沒有設(shè)置保存點,也可以回滾,只能回滾到事務(wù)最開始。直接使用rollback,前提是事務(wù)還沒有提交。
  • 如果一個事務(wù)被提交(commit),則不可以回滾。
  • 可以選擇回滾到哪個保存點。
  • Innodb可以支持事務(wù),MyISAM不支持事務(wù)。
  • 手動開始事務(wù)使用begin,或start? transaction。

?二.隔離級別

? ? ? ? 再次說明,MySQL是一個網(wǎng)絡(luò)服務(wù),同一時間可能有很多客戶端連接。那么在多個事務(wù)在執(zhí)行多個SQL時,有可能出現(xiàn)問題,比如:多個事務(wù)訪問同一張表,同一行數(shù)據(jù)時。

? ? ? ? 一個事務(wù)的時間段可以分為執(zhí)行前(該事務(wù)還沒有開始),執(zhí)行中(該事務(wù)正在執(zhí)行),執(zhí)行后(該事務(wù)已經(jīng)提交)。

? ? ? ? 隔離性:保證了事務(wù)執(zhí)行過程中盡量不受干擾。

? ? ? ? 為了提高效率,執(zhí)行過程并不是串行化,而是允許事務(wù)受到不同程度的干擾,于是就有了一個重要的特征:隔離級別。

? ? ? ? 2.1 隔離級別介紹

? ? ? ? 注意:隔離級別是針對事務(wù)之間的。

  • 讀未提交(read uncommitted):在該隔離級別下,所有事務(wù)都能看到其它事務(wù)沒有提交的執(zhí)行結(jié)果。相當(dāng)于沒有任何隔離性。在實際生產(chǎn)中不會使用這一隔離級別,因為會有很多并發(fā)問題,如臟讀,幻讀,不可重復(fù)度等。
  • 讀提交(read committed):事務(wù)只能看到其它事務(wù)已經(jīng)提交后執(zhí)行結(jié)果。這種隔離級別是大多數(shù)數(shù)據(jù)庫的默認(rèn)隔離級別(不是MySQL的),這種隔離級別會引起不可重復(fù)讀的問題。
  • 可重復(fù)度(repeatable? read):在該隔離級別下,在一時間段同時運行的事務(wù),執(zhí)行中,看不到其它事務(wù)已經(jīng)提交的執(zhí)行結(jié)果,必須等事務(wù)執(zhí)行完才能看到其它事務(wù)提交后執(zhí)行結(jié)果??赡軙霈F(xiàn)幻讀的問題。這是MySQL的默認(rèn)隔離級別。
  • 串行化(serialization):強制事務(wù)在增刪改同一張表的同一行時排序執(zhí)行,使之不可能相互沖突。實際,它是在訪問的每一個數(shù)據(jù)行上加了共享鎖。這是事務(wù)最高隔離級別。但是這個的效率很低,可能導(dǎo)致超時和鎖競爭。(這種隔離太極端,生活中基本不使用)

? ? ? ? 隔離基本上都是通過加鎖來實現(xiàn)的,不同隔離級別,鎖的使用是不同的。常見的有,表鎖,行鎖,讀鎖,寫鎖,間隙鎖,Next-key鎖(GAP+行鎖)。

讀提交和可重復(fù)讀的區(qū)別:

?????????有兩個事務(wù),事務(wù)A和事務(wù)B。

? ? ? ? 事務(wù)A修改數(shù)據(jù),提交后(commit)。讀提交,事務(wù)B在提交前(commit)可以看到事務(wù)A修改后的數(shù)據(jù)??芍貜?fù)讀,事務(wù)B在提交前(commit)看不到事務(wù)A修改后的數(shù)據(jù),事務(wù)B在提交后,才能看到。

? ? ? ? 2.2 查看和設(shè)置隔離性

查看隔離性:

?設(shè)置隔離等級:

? ? ? ? 這是設(shè)置當(dāng)前會話或者全局隔離級別的語法。

set? [session |? global]? transaction? isolation? level? ?{READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE};

設(shè)置當(dāng)前會話隔離性,另起一個會話,隔離性不會被設(shè)置,只影響當(dāng)前會話。

?設(shè)置全局隔離級別,另起一個會話,會被影響。

?會話隔離級別開始啟動時,會和全局隔離級別一樣。

?設(shè)置了全局會話隔離級別,當(dāng)前會話隔離級別并沒有改變,需要重啟MySQL才會改變當(dāng)前會話隔離級別。

?修改當(dāng)前會話隔離級別:

  • 直接修改會話隔離級別
  • 修改全局隔離級別,重啟MySQL

? ? ? ? 2.3 操作演示

? ? ? ? 隔離級別時針對事務(wù)和事務(wù)之間的。

? ? ? ? 2.3.1 讀未提交

? ? ? ? 現(xiàn)象:如果隔離級別為讀未提交,一個事務(wù)A增刪改表的內(nèi)容,未提交事務(wù),在另外一個事務(wù)B中可以看到變化的內(nèi)容。

? ? ? ? 但是,如果事務(wù)A沒有提交異常退出了,內(nèi)容會發(fā)生回滾,回滾到事務(wù)開始前的內(nèi)容,原因是事務(wù)的原子性。事務(wù)A提交了,事務(wù)B,提交后也可以看到修改之后的內(nèi)容,這是應(yīng)為事務(wù)的持久性。

? ? ? ? ?一個事務(wù)執(zhí)行中,讀到了另外一個事務(wù)的增刪改,但是沒有commit的數(shù)據(jù),這種現(xiàn)象叫做臟讀。

? ? ? ? 2.3.2 讀提交

? ? ? ? 現(xiàn)象:兩個事務(wù),事務(wù)A和事務(wù)B,隔離等級為讀提交。事務(wù)A增刪改表的內(nèi)容,未提交事務(wù),在事務(wù)B中,看不到增刪改的內(nèi)容。當(dāng)事務(wù)A提交后,事務(wù)B中可以看到增刪改的內(nèi)容。注意事務(wù)B沒有提交。

? ? ? ? 問題:此時在事務(wù)B中,沒有提交事務(wù)。但是同樣的讀取,在同一個事務(wù)內(nèi),在不同的時間段,讀取到的值不同,這種現(xiàn)在叫做不可重復(fù)讀取。

? ? ? ? ?2.3.3 可重復(fù)讀

? ? ? ? 現(xiàn)象:兩個事務(wù),事務(wù)A和事務(wù)B。事務(wù)A增刪改表的內(nèi)容,未提交,事務(wù)B看不到修改后的數(shù)據(jù);事務(wù)A提交后,事務(wù)B沒有提交,仍然看不到提交的數(shù)據(jù);事務(wù)B提交后,由于持久化,可以看到修改后的數(shù)據(jù)。

? ? ? ? 注意:并不是,同一時段的兩個事務(wù),事務(wù)A提交的數(shù)據(jù),事務(wù)B就一定看不到。這個取決于select 的位置。下面詳細(xì)解釋了。

問題:

? ? ? ? 幻讀:在事務(wù)只執(zhí)行中,不同時間段查詢,會查找出來新的記錄。即,事務(wù)A插入一條數(shù)據(jù),事務(wù)B在未提交前,看到了插入的數(shù)據(jù)。

? ? ? ? 但是,在下面的演示中,在事務(wù)A向表中插入數(shù)據(jù),事務(wù)B中在未提交前沒有看到插入的數(shù)據(jù)。這是因為MySQL解決了幻讀的問題。

但是,一般的數(shù)據(jù)庫,在事務(wù)未提交前能夠讀到其它數(shù)據(jù)插入的數(shù)據(jù)。這是為什么呢?

? ? ? ? 因為隔離性實現(xiàn)是對數(shù)據(jù)加鎖實現(xiàn)的,而insert數(shù)據(jù),插入的數(shù)據(jù),在表中并不存在,一般的加鎖無法屏蔽這里問題。

而MySQL是如何解決的呢?

? ? ? ? MySQL用的是Next-Key鎖(GAP+行鎖)解決的。

? ? ? ? ?2.3.4 串行化

? ? ? ? 現(xiàn)象:當(dāng)前有多個事務(wù)時,一個事務(wù)要增刪改表的數(shù)據(jù),會發(fā)生阻塞。當(dāng)其它數(shù)據(jù)全部退出,才能正常進行增刪改操作。使得增刪改時,事務(wù)之間運行是串行的。效率比較低。

? ? ? ? 但是:如下圖,我們發(fā)現(xiàn),查找,并沒有串行化,有多個事務(wù)同時運行時,查找不會被阻塞,串行化。這是因為查詢并不會修改數(shù)據(jù)。

????????2.3.5 總結(jié)

  • 隔離等級越高,安全性越高,并發(fā)性越低,因為加鎖。往往要在兩者之間找一個平衡點。
  • 不可重復(fù)讀是,同一條件下,事務(wù)在執(zhí)行過程中,不同時間段,讀取的數(shù)據(jù)不同。
  • 幻讀是,同樣條件下,事務(wù)在執(zhí)行過程中,不同時間段,讀出來,數(shù)據(jù)量比之前增加了。
  • 臟讀:同樣條件下,事務(wù)在執(zhí)行過程中,讀到了其它事務(wù)修改的數(shù)據(jù)。
  • 事務(wù)也有長短的概念,事務(wù)之間相互影響,指的是,在事務(wù)執(zhí)行過程中,都沒有提交(commit),影響會比較大。

針對MySQL:

隔離級別臟讀幻讀不可重復(fù)讀
讀未提交【Read Uncommitted
讀提交【Read Committed 沒有
可重復(fù)讀【Repeatable Read 沒有沒有沒有
串行化【serializable 沒有沒有沒有

注意:

? ? ? ? 當(dāng)多個事務(wù),同時進行update, insert或者delete時,是會有加鎖現(xiàn)象的。即,會發(fā)生阻塞。但是select查詢和增刪改并不沖突,即不會發(fā)生阻塞。這是通過讀寫鎖(鎖由行鎖或者表鎖)+MVCC完成的。

? ? ? ? 這里不好演示,直接給了結(jié)論。

? ? ? ? 2.4 一致性

  • 事務(wù)執(zhí)行結(jié)果,必須使得數(shù)據(jù)庫從一個一致性的結(jié)果,變成另外一個一致性狀態(tài)。如果系統(tǒng)運行時發(fā)生中斷,某個事務(wù),被迫中斷,而未完成事務(wù)對數(shù)據(jù)的修改,此時數(shù)據(jù)庫出于一種不一致的狀態(tài)。
  • 一致性性就是,在事務(wù)開始前,數(shù)據(jù)是一致的,事務(wù)完成后,數(shù)據(jù)也是一致的。
  • 其實一致性和用戶業(yè)務(wù)的邏輯性相關(guān),需要MySQL提供技術(shù)支持,保證數(shù)據(jù)不會出錯。還需要用戶業(yè)務(wù)邏輯上的支持,在MySQL文檔中,一致性,是由用戶來決定的。
  • 技術(shù)上,數(shù)據(jù)庫的原子性,隔離性,持久性保證了一致性。

三.解決并發(fā)的原理MVCC

? ? ? ? 對數(shù)據(jù)庫操作實際上就是對數(shù)據(jù)庫進行讀和寫的操作,并發(fā)就是,多個事務(wù)同時對數(shù)據(jù)庫進行讀和寫操作。而數(shù)據(jù)庫正是對在效率和安全方面的考慮,解決讀和寫并發(fā)的問題。

? ? ? ? 3.1 數(shù)據(jù)庫并發(fā)的三種場景

  • 讀和讀:不會存在任何問題,不需要并發(fā)的控制。不會修改數(shù)據(jù)。
  • 寫和寫:有安全問題,可能會存在數(shù)據(jù)更新丟失,比如:第一類更新丟失,第二類更新丟失。如事務(wù)A更新了數(shù)據(jù),事務(wù)B回滾導(dǎo)致事務(wù)A更新的數(shù)據(jù)丟失了。需要加鎖,串行運行。
  • 讀和寫:有安全問題,但是為了效率的考慮,不一定加鎖,串行運行??偤涂紤]效率和安全,所以有了隔離等級,但是,仍然可能有其它問題,臟讀,幻讀,不可重復(fù)讀的問題。

讀和讀,寫和寫的問題好處理,但是讀和寫的問題就比較麻煩,下面主要討論如何解決讀和寫并發(fā)問題。

? ? ? ? 3.2 讀和寫

? ? ? ? 多版本并發(fā)控制(Multiversion concurrency control),簡稱MVCC,是一種解決讀寫沖突的無鎖并發(fā)控制。

? ? ? ? 主要是:為事務(wù)分配單向增長的事務(wù)ID,為每一個修改保存一個版本,版本與事務(wù)ID關(guān)聯(lián),讀操作只讀改事務(wù)開始前的數(shù)據(jù)的快照(快照,后面有解釋,需要先理解,才能理解)。所以MVCC可以為數(shù)據(jù)庫解決以下問題:

  • 在并發(fā)讀寫數(shù)據(jù)庫時,可以做到在讀操作時,不用阻塞寫操作,寫操作時也不用阻塞讀操作,因為讀寫的數(shù)據(jù)不是一個版本。提高了數(shù)據(jù)庫并發(fā)讀寫的性能。
  • 同時還解決了臟讀,幻讀,不可重復(fù)讀等事務(wù)隔離問題,但不能解決更新丟失問題。

注意:沒有同時啟動的事務(wù),事務(wù)的啟動一定會有先后的。所以每個事務(wù)的ID一定不同。

想要理解MVCC需要知道三個前提知識:

  • 3個記錄隱藏字段
  • undo日志
  • read view

? ? ? ? 3.2.1 三個隱藏字段

? ? ? ? 當(dāng)我們在數(shù)據(jù)庫中創(chuàng)建一個表時,表中的字段不僅會有我們設(shè)計的字段,還有有幾個隱藏字段。

這里主要介紹三個:

  • DB_TRX_ID:6字節(jié),保存最近修改(修改/插入)改數(shù)據(jù)的事務(wù)的ID
  • DB_ROLL_PTR:7字節(jié),回滾指針,執(zhí)行這條記錄的上一個版本。
  • DB_ROW_ID:6字節(jié),隱含的自增ID(隱藏主鍵)。在innodb存儲引擎中,如果數(shù)據(jù)沒有主鍵,innodb會自動創(chuàng)建一個隱藏主鍵來構(gòu)建索引。這個隱藏主鍵就是DB_ROW_ID字段記錄的主鍵。

補充:實際上還會有一個刪除flag隱藏字段,即記錄刪除。實際上的刪除一個字段,并不是真正的刪除,而是將flag設(shè)置為刪除標(biāo)記。

演示:

?此時:表的全部信息為:

? ? ? ? ?我們目前并不知道創(chuàng)建該記錄的事務(wù)ID,隱式主鍵。默認(rèn)設(shè)置成NULL和1。第一條記錄之前也沒有其它版本,我們設(shè)置回滾指針為NULL。

? ? ? ? 3.2.2 undo日志

? ? ? ? undo日志,實際上時MySQL內(nèi)存緩沖區(qū)里的一段空間,即buffer pool中的一段空間,用來保存日志數(shù)據(jù),之后再刷新到磁盤中。

? ? ? ? MySQL是一個服務(wù)進程,所有操作都需要在內(nèi)存中完成的。比如:修改數(shù)據(jù),先將數(shù)據(jù)拿到內(nèi)存中,修改后,再刷新到磁盤中。日志也是一樣,先將日志信息寫到MySQL內(nèi)部緩沖區(qū)中,之后再刷新到磁盤中。

? ? ? ? ?3.2.3 模擬MVCC過程

說明:現(xiàn)在有一個事務(wù),事務(wù)ID為10,對student表中的字段做的修改(update),將name從張三,修改成了李四。

過程:

  • 事務(wù)10,因為要修改字段,先給記錄加上行鎖。防止有其它記錄同時修改。
  • 修改前,先將當(dāng)前記錄拷貝到undo log中。
  • 所以現(xiàn)在MySQL中有了兩行相同的記錄。現(xiàn)在修改原始記錄,將名字改為李四。
  • 修改隱藏字段DB_TRX_ID為當(dāng)前事務(wù)10的事務(wù)ID,
  • 修改回滾指針DB_ROLL_PTR,里面填入之前拷貝數(shù)據(jù)的起始地址,從而指向副本數(shù)據(jù),表明這是上一個版本。
  • 事務(wù)10提交,釋放鎖。

?現(xiàn)在又有一個事務(wù)11,對student表進行修改(update):將年齡age由28改成38。

注意:修改,修改的是最新記錄。

  • 事務(wù)11,因為要修改,給最新記錄加上行鎖。
  • 修改前,將當(dāng)前記錄拷貝到undo log中,產(chǎn)生新的副本,我們采用頭插的方式,插入到undo log中。
  • 現(xiàn)在修改原始記錄中的年齡,改成38。
  • 修改隱藏字段DB_TRX_ID為當(dāng)前事務(wù)ID11.
  • 修改回滾指針DB_ROLL_PTR列,保存undo log中新副本的起始地址,從而指向副本記錄,表示上一個版本就是它。
  • 事務(wù)11提交,釋放鎖。

?????????于是這樣就是形成了一個基于鏈表記錄的版本鏈。所謂回滾,就是,用歷史數(shù)據(jù),覆蓋當(dāng)前數(shù)據(jù)。

? ? ? ? 快照:就是undo log中的一個個版本。

????????注意:只有在增刪改動作的時候,才會形成快照。

上面主要講的是修改數(shù)據(jù)undo log

如果是delete呢?刪除數(shù)據(jù)了,這個字段就沒有了,如何生成這個字段的快照呢?

? ? ? ? 上面補充的時候說了,在表中還有一個隱藏字段flag,來標(biāo)記當(dāng)前字段是否被刪除。所以刪除數(shù)據(jù),并不是將數(shù)據(jù)真正刪除了,而是將flag字段,設(shè)置成了刪除標(biāo)記。所以,刪除字段也可以生成快照,保存在undo log中。

如果是insert呢?插入數(shù)據(jù),之前沒有插入數(shù)據(jù)前面沒有歷史版本,如果要回滾到插入前怎么辦的?

? ? ? ? 起始在MySQL中由記錄數(shù)據(jù)的log,就像上面一樣。還由基于語句的log,記錄相反的語句。比如:insert一條數(shù)據(jù),在log中就會記錄delete的語句。

如果是select呢?

? ? ? ? select不會對數(shù)據(jù)進行修改,所以不需要為select維護多版本。

select讀取的時候,是讀取的最新數(shù)據(jù)還是,快照呢?

? ? ? ? select既可以讀取最新數(shù)據(jù),也可以讀取快照。

? ? ? ? 當(dāng)前讀:讀取最新數(shù)據(jù)。增刪改,都是當(dāng)前讀,因為需要修改最新的數(shù)據(jù)。select也可以當(dāng)前讀,(select? lock? in share? mode,select? for? update),select當(dāng)前讀時,就需要對數(shù)據(jù)加鎖,因為增刪改也是當(dāng)前讀,避免數(shù)據(jù)錯誤。

? ? ? ? 快照讀:讀取快照。增刪改是當(dāng)前讀,如果select采用快照讀,這樣不需要加鎖,可以實現(xiàn)讀寫并發(fā),這就是MVCC的意義所在。

那什么決定了select快照讀還是當(dāng)前讀?

? ? ? ??隔離級別決定的。不同的隔離級別,訪問的數(shù)據(jù)版本不同。

? ? ? ? 比如:讀提交:每次都可以訪問到修改后的數(shù)據(jù),當(dāng)前讀。讀未提交:訪問不到需改,但是未提交的數(shù)據(jù),快照讀。

事務(wù)操作哪個版本是在事務(wù)啟動時確定的,事務(wù)總有先后。事務(wù)啟動時,會分配一個事務(wù)id,通過對比事務(wù)id來確定操作哪個快照。

? ? ? ? 3.2.4 read view

? ? ? ? Read? view是事務(wù)進行快照讀操作的時候生產(chǎn)的讀視圖(read view)。決定了這個select可以看到多少版本數(shù)據(jù)。這個讀視圖中,維護了系統(tǒng)當(dāng)前活躍事務(wù)的id。

? ? ? ? 簡單來說就是,在我們某個事務(wù)進行快照讀時,對該記錄創(chuàng)建一個read view,把他當(dāng)作條件,用來判斷當(dāng)前事務(wù)能夠看到哪個版本的數(shù)據(jù)。即,可能時最新版本,也可能是undo log中的版本。

在read view中主要由以下四個成員:

  • m_ids:相當(dāng)于一個數(shù)組。記錄read view生成時刻,系統(tǒng)正在執(zhí)行的事務(wù)。
  • up_limit_id:記錄m_ids列表中事務(wù)最小的ID。
  • low_limit_id:read view生成時刻,系統(tǒng)尚未分配的下一個事務(wù)ID。
  • creator_trx_id:創(chuàng)建read? view的事務(wù)id。

? ? ? ? 我們讀取數(shù)據(jù)版本鏈時,能夠獲得每一個版本對應(yīng)的事務(wù)ID。即:DB_TRX_ID。

????????我們只需要拿read_view和DB_TRX_ID對比,就可以知道可以讀取的id。

????????最終根據(jù)隔離級別,可以獲得要讀取的id。

  • Creator_trx_id == DB_TRX_ID || DB_TRX_ID < up_limit_id

?????????在形成read view前已經(jīng)提交的事務(wù),形成的版本鏈數(shù)據(jù),可以看到。

  • Up_limit_id <= DB_TRX_ID < low_limit_id

????????在形成read view時,正在執(zhí)行的事務(wù)id保存在m_ids中。不在m_id中說明在形成快照前提交了,但是仍然在up_limit_id和low_limit_id范圍內(nèi)。

????????注意:m_ids中的事務(wù)id不一定是連續(xù)的,可能由中間的事務(wù)ID已經(jīng)提交了。比如:我們有11,12,13,14,15號事務(wù)。在形成快照前12,14號事務(wù)提交了。形成快照后,m_ids中保存的就是11,13,15。up_limit_id等于11,low_limit_id等于16。

????????此時如果DB_TRX_ID沒有m_ids中,說明已經(jīng)提交(commit),可以被查詢。

? ? ? ? 如果DB_TRX_ID在m_ids中,說明還沒有被提交,不可以被查詢,

? ? ? ? 看下面源代碼就懂了。并且可重復(fù)讀和讀提交,是因為read? view不同,所以呈現(xiàn)的現(xiàn)象不同。下面有詳細(xì)介紹。

  • DB_TRX_ID >= low_limit_id

? ? ? ? 說明是形成read view后,形成的事務(wù),對數(shù)據(jù)進行了修改。插入到了版本鏈中。

????????比如:此時有11,12,13,14,15號事務(wù),形成read view。up_limit_id等于11,low_limit_id等于16。之后,又開始了一個事務(wù)16,并且修改了數(shù)據(jù),所以版本鏈中會有大于low_limit_id的版本數(shù)據(jù)。

? ? ? ? 這種數(shù)據(jù)不能被查看。

?對應(yīng)源碼策略:

  • ?id就是版本鏈中的DB_TRX_ID
  • 當(dāng)Up_limit_id <= DB_TRX_ID < low_limit_id,只要m_ids中沒有id,就可以被查詢。?

注意:

  • read? veiw的作用就是用來判斷版本鏈中的數(shù)據(jù)是否可以看到。
  • 從版本鏈第一個結(jié)點開始查看,如果查到不應(yīng)該看到的版本,就遍歷下一個版本,直到可以查看。
  • read view是進行可見性判斷的,即有了read view才直到哪些版本鏈的版本可見,哪些版本鏈的版本不可見。
  • read view是在select時,自動生成的。

MVCC流程演示,方便理解:

假設(shè)當(dāng)前有一條記錄:

?事務(wù)操作:

事務(wù)1(id = 1)事務(wù)2(id = 2)事務(wù)3(id = 3)事務(wù)4(id = 4)
事務(wù)開始事務(wù)開始事務(wù)開始事務(wù)開始
.........修改并提交
進行中快照讀進行中
.........

事務(wù)修改了name,將name有張三修改成了李四。

當(dāng)事務(wù)2對某行數(shù)據(jù)進行快照讀,數(shù)據(jù)庫為改行數(shù)據(jù)形成移和read view讀視圖。

該read view中保存的數(shù)據(jù)為:????????

????????m_id2 = {1,3};

????????up_limit_id = 1;

????????low_limit_id = 5;

????????creator_trx_id = 2;

版本鏈為:

只有事務(wù)4修改了數(shù)據(jù),并在事務(wù)2快照讀前,提交了事務(wù)。

?事務(wù)2拿著版本鏈的DB_TRX_ID去跟up_limit_id和low_limit_id和活躍事務(wù)ID列表進行比較,判斷當(dāng)前版本是否可以查看。

此時

該read view中保存的數(shù)據(jù)為:????????

????????m_id2 = {1,3};

????????up_limit_id = 1;

????????low_limit_id = 5;

????????creator_trx_id = 2;

DB_TRX_ID = 4

比較步驟:

  • DB_TRX_ID(4)? < up_limit_id(1)? 不小于,判斷下一個條件。如果小于可以查看。
  • DB_TRX_ID(4)?>= low_limit_id(5) ? 不大于,判斷寫一個條件。如果大于,不可以查看,遍歷版本鏈中的下一個結(jié)點。
  • m_ids.contains(DB_TRX_ID) ? 不包含,說明,事務(wù)4不在當(dāng)前的活躍事務(wù)中。 此時就需要隔離級別來決定是否可以查看。 隔離級別為讀提交,可以查看,可重復(fù)讀,不可以查看。

?四.RR和RC的本質(zhì)區(qū)別

?????????RR是可重復(fù)度,RC是讀提交。

? ? ? ? 對于表:

?設(shè)置當(dāng)前會話的隔離級別為可重復(fù)讀

?介紹一個當(dāng)前讀語法:

????????select *? ?from? ?表名? lock in share mode

案例1:?

事務(wù)A操作事務(wù)A描述事務(wù)B描述事務(wù)B操作
begin開啟事務(wù)開啟事務(wù)begin
select * from? student快照讀快照讀select * from? student
update student set age=30 where name='張三'更新名字為張三的年齡為30
commit提交事務(wù)
select沒有讀到張三的年齡為30select * from? student
select可以讀到張三的年齡為30select *? ?from? ?student? lock in share mode

?案例2:?

事務(wù)A操作事務(wù)A描述事務(wù)B描述事務(wù)B操作
begin開啟事務(wù)開啟事務(wù)begin
select * from? student快照讀,查到年齡為28
update student set age=30 where name='張三'更新名字為張三的年齡為30
commit提交事務(wù)
select可以讀到張三的年齡為30select * from? student
select可以讀到張三的年齡為30select *? ?from? ?student? lock in share mode

兩測試用例的區(qū)別:

  • 案例1在事務(wù)A提交和修改前,快照讀了一次。
  • 案例2在事務(wù)提交和修改前,沒有快照讀。

結(jié)論:

針對隔離級別是可重復(fù)讀(repeatable? read)

  • 事務(wù)中快照讀的結(jié)果是非常依賴該事務(wù)首次出現(xiàn)快照讀的地方,即某個事務(wù)中首次出現(xiàn)快照讀,決定該事務(wù)后續(xù)快照讀結(jié)果的能力。即,此時版本鏈中有多少結(jié)點。
可重復(fù)讀和讀提交本質(zhì)區(qū)別: ??????? 首先強調(diào): ????????源代碼中,如果Up_limit_id <= DB_TRX_ID < low_limit_id,只要不在m_ids中就可以被查詢。 ? ? ? ? read view是在快照讀的時候才形成的。
  • 在RR下,某個事務(wù),只會對某條記錄的第一次快照讀會創(chuàng)建一個快照及Read view。
  • 之后在調(diào)用快照讀時,還是使用的同一個read? view,對形成read view之后的的修改是不可見的。
  • 在RC下,事務(wù)中,每一次快照讀都會新生成一個快照和read? view,這就是在RC級別下的事務(wù)中可以看到別的事務(wù)提交的更新的原因。
  • RR和RC區(qū)別是因為read? view的不同,RR只在第一次形成一個read veiw,并且之后都只會看這一個,而RC每一次快照讀都會形成read? view。

總結(jié)

以上是生活随笔為你收集整理的MySQL事务——万字详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。