溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

mysql事務(wù)指的是什么

發(fā)布時(shí)間:2022-02-18 11:12:06 來源:億速云 閱讀:145 作者:iii 欄目:MySQL數(shù)據(jù)庫

本文小編為大家詳細(xì)介紹“mysql事務(wù)指的是什么”,內(nèi)容詳細(xì),步驟清晰,細(xì)節(jié)處理妥當(dāng),希望這篇“mysql事務(wù)指的是什么”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學(xué)習(xí)新知識(shí)吧。

在mysql中,事務(wù)是一種機(jī)制、一個(gè)操作序列,是訪問和更新數(shù)據(jù)庫的程序執(zhí)行單元。事務(wù)中包含一個(gè)或多個(gè)數(shù)據(jù)庫操作命令,會(huì)把所有的命令作為一個(gè)整體一起向系統(tǒng)提交或撤銷操作請求,即這一組數(shù)據(jù)庫命令要么都執(zhí)行,要么都不執(zhí)行。

本教程操作環(huán)境:windows7系統(tǒng)、mysql5.6版本、Dell G3電腦。

數(shù)據(jù)庫的事務(wù)(Transaction)是一種機(jī)制、一個(gè)操作序列,是訪問和更新數(shù)據(jù)庫的程序執(zhí)行單元,包含了一組數(shù)據(jù)庫操作命令。

事務(wù)把所有的命令作為一個(gè)整體一起向系統(tǒng)提交或撤銷操作請求,即這一組數(shù)據(jù)庫命令要么都執(zhí)行,要么都不執(zhí)行,因此事務(wù)是一個(gè)不可分割的工作邏輯單元。

在數(shù)據(jù)庫系統(tǒng)上執(zhí)行并發(fā)操作時(shí),事務(wù)是作為最小的控制單元來使用的,特別適用于多用戶同時(shí)操作的數(shù)據(jù)庫系統(tǒng)。

作為一個(gè)關(guān)系型數(shù)據(jù)庫,MySQL支持事務(wù),本文介紹基于MySQL5.6。

首先回顧一下MySQL事務(wù)的基礎(chǔ)知識(shí)。

1. 邏輯架構(gòu)和存儲(chǔ)引擎

mysql事務(wù)指的是什么

如上圖所示,MySQL服務(wù)器邏輯架構(gòu)從上往下可以分為三層:

(1)第一層:處理客戶端連接、授權(quán)認(rèn)證等。

(2)第二層:服務(wù)器層,負(fù)責(zé)查詢語句的解析、優(yōu)化、緩存以及內(nèi)置函數(shù)的實(shí)現(xiàn)、存儲(chǔ)過程等。

(3)第三層:存儲(chǔ)引擎,負(fù)責(zé)MySQL中數(shù)據(jù)的存儲(chǔ)和提取。MySQL中服務(wù)器層不管理事務(wù),事務(wù)是由存儲(chǔ)引擎實(shí)現(xiàn)的。MySQL支持事務(wù)的存儲(chǔ)引擎有InnoDB、NDB Cluster等,其中InnoDB的使用最為廣泛;其他存儲(chǔ)引擎不支持事務(wù),如MyIsam、Memory等。

如無特殊說明,后文中描述的內(nèi)容都是基于InnoDB。

2. 提交和回滾

典型的MySQL事務(wù)是如下操作的:

start transaction;
……  #一條或多條sql語句
commit;

其中start transaction標(biāo)識(shí)事務(wù)開始,commit提交事務(wù),將執(zhí)行結(jié)果寫入到數(shù)據(jù)庫。如果sql語句執(zhí)行出現(xiàn)問題,會(huì)調(diào)用rollback,回滾所有已經(jīng)執(zhí)行成功的sql語句。當(dāng)然,也可以在事務(wù)中直接使用rollback語句進(jìn)行回滾。

自動(dòng)提交

MySQL中默認(rèn)采用的是自動(dòng)提交(autocommit)模式,如下所示:

mysql事務(wù)指的是什么

在自動(dòng)提交模式下,如果沒有start transaction顯式地開始一個(gè)事務(wù),那么每個(gè)sql語句都會(huì)被當(dāng)做一個(gè)事務(wù)執(zhí)行提交操作。

通過如下方式,可以關(guān)閉autocommit;需要注意的是,autocommit參數(shù)是針對連接的,在一個(gè)連接中修改了參數(shù),不會(huì)對其他連接產(chǎn)生影響。

mysql事務(wù)指的是什么

如果關(guān)閉了autocommit,則所有的sql語句都在一個(gè)事務(wù)中,直到執(zhí)行了commit或rollback,該事務(wù)結(jié)束,同時(shí)開始了另外一個(gè)事務(wù)。

特殊操作

在MySQL中,存在一些特殊的命令,如果在事務(wù)中執(zhí)行了這些命令,會(huì)馬上強(qiáng)制執(zhí)行commit提交事務(wù);如DDL語句(create table/drop table/alter/table)、lock tables語句等等。

不過,常用的select、insert、update和delete命令,都不會(huì)強(qiáng)制提交事務(wù)。

3. ACID特性

ACID是衡量事務(wù)的四個(gè)特性:

  • 原子性(Atomicity,或稱不可分割性)

  • 一致性(Consistency)

  • 隔離性(Isolation)

  • 持久性(Durability)

按照嚴(yán)格的標(biāo)準(zhǔn),只有同時(shí)滿足ACID特性才是事務(wù);但是在各大數(shù)據(jù)庫廠商的實(shí)現(xiàn)中,真正滿足ACID的事務(wù)少之又少。例如MySQL的NDB Cluster事務(wù)不滿足持久性和隔離性;InnoDB默認(rèn)事務(wù)隔離級別是可重復(fù)讀,不滿足隔離性;Oracle默認(rèn)的事務(wù)隔離級別為READ COMMITTED,不滿足隔離性……因此與其說ACID是事務(wù)必須滿足的條件,不如說它們是衡量事務(wù)的四個(gè)維度。

下面將詳細(xì)介紹ACID特性及其實(shí)現(xiàn)原理;為了便于理解,介紹的順序不是嚴(yán)格按照A-C-I-D。

原子性

1. 定義

原子性是指一個(gè)事務(wù)是一個(gè)不可分割的工作單位,其中的操作要么都做,要么都不做;如果事務(wù)中一個(gè)sql語句執(zhí)行失敗,則已執(zhí)行的語句也必須回滾,數(shù)據(jù)庫退回到事務(wù)前的狀態(tài)。

2. 實(shí)現(xiàn)原理:undo log

在說明原子性原理之前,首先介紹一下MySQL的事務(wù)日志。MySQL的日志有很多種,如二進(jìn)制日志、錯(cuò)誤日志、查詢?nèi)罩?、慢查詢?nèi)罩镜?,此外InnoDB存儲(chǔ)引擎還提供了兩種事務(wù)日志:redo log(重做日志)和undo log(回滾日志)。其中redo log用于保證事務(wù)持久性;undo log則是事務(wù)原子性和隔離性實(shí)現(xiàn)的基礎(chǔ)。

下面說回undo log。實(shí)現(xiàn)原子性的關(guān)鍵,是當(dāng)事務(wù)回滾時(shí)能夠撤銷所有已經(jīng)成功執(zhí)行的sql語句。InnoDB實(shí)現(xiàn)回滾,靠的是undo log:當(dāng)事務(wù)對數(shù)據(jù)庫進(jìn)行修改時(shí),InnoDB會(huì)生成對應(yīng)的undo log;如果事務(wù)執(zhí)行失敗或調(diào)用了rollback,導(dǎo)致事務(wù)需要回滾,便可以利用undo log中的信息將數(shù)據(jù)回滾到修改之前的樣子。

undo log屬于邏輯日志,它記錄的是sql執(zhí)行相關(guān)的信息。當(dāng)發(fā)生回滾時(shí),InnoDB會(huì)根據(jù)undo log的內(nèi)容做與之前相反的工作:對于每個(gè)insert,回滾時(shí)會(huì)執(zhí)行delete;對于每個(gè)delete,回滾時(shí)會(huì)執(zhí)行insert;對于每個(gè)update,回滾時(shí)會(huì)執(zhí)行一個(gè)相反的update,把數(shù)據(jù)改回去。

以update操作為例:當(dāng)事務(wù)執(zhí)行update時(shí),其生成的undo log中會(huì)包含被修改行的主鍵(以便知道修改了哪些行)、修改了哪些列、這些列在修改前后的值等信息,回滾時(shí)便可以使用這些信息將數(shù)據(jù)還原到update之前的狀態(tài)。

持久性

1. 定義

持久性是指事務(wù)一旦提交,它對數(shù)據(jù)庫的改變就應(yīng)該是永久性的。接下來的其他操作或故障不應(yīng)該對其有任何影響。

2. 實(shí)現(xiàn)原理:redo log

redo log和undo log都屬于InnoDB的事務(wù)日志。下面先聊一下redo log存在的背景。

InnoDB作為MySQL的存儲(chǔ)引擎,數(shù)據(jù)是存放在磁盤中的,但如果每次讀寫數(shù)據(jù)都需要磁盤IO,效率會(huì)很低。為此,InnoDB提供了緩存(Buffer Pool),Buffer Pool中包含了磁盤中部分?jǐn)?shù)據(jù)頁的映射,作為訪問數(shù)據(jù)庫的緩沖:當(dāng)從數(shù)據(jù)庫讀取數(shù)據(jù)時(shí),會(huì)首先從Buffer Pool中讀取,如果Buffer Pool中沒有,則從磁盤讀取后放入Buffer Pool;當(dāng)向數(shù)據(jù)庫寫入數(shù)據(jù)時(shí),會(huì)首先寫入Buffer Pool,Buffer Pool中修改的數(shù)據(jù)會(huì)定期刷新到磁盤中(這一過程稱為刷臟)。

Buffer Pool的使用大大提高了讀寫數(shù)據(jù)的效率,但是也帶了新的問題:如果MySQL宕機(jī),而此時(shí)Buffer Pool中修改的數(shù)據(jù)還沒有刷新到磁盤,就會(huì)導(dǎo)致數(shù)據(jù)的丟失,事務(wù)的持久性無法保證。

于是,redo log被引入來解決這個(gè)問題:當(dāng)數(shù)據(jù)修改時(shí),除了修改Buffer Pool中的數(shù)據(jù),還會(huì)在redo log記錄這次操作;當(dāng)事務(wù)提交時(shí),會(huì)調(diào)用fsync接口對redo log進(jìn)行刷盤。如果MySQL宕機(jī),重啟時(shí)可以讀取redo log中的數(shù)據(jù),對數(shù)據(jù)庫進(jìn)行恢復(fù)。redo log采用的是WAL(Write-ahead logging,預(yù)寫式日志),所有修改先寫入日志,再更新到Buffer Pool,保證了數(shù)據(jù)不會(huì)因MySQL宕機(jī)而丟失,從而滿足了持久性要求。

既然redo log也需要在事務(wù)提交時(shí)將日志寫入磁盤,為什么它比直接將Buffer Pool中修改的數(shù)據(jù)寫入磁盤(即刷臟)要快呢?主要有以下兩方面的原因:

(1)刷臟是隨機(jī)IO,因?yàn)槊看涡薷牡臄?shù)據(jù)位置隨機(jī),但寫redo log是追加操作,屬于順序IO。

(2)刷臟是以數(shù)據(jù)頁(Page)為單位的,MySQL默認(rèn)頁大小是16KB,一個(gè)Page上一個(gè)小修改都要整頁寫入;而redo log中只包含真正需要寫入的部分,無效IO大大減少。

3. redo log與binlog

我們知道,在MySQL中還存在binlog(二進(jìn)制日志)也可以記錄寫操作并用于數(shù)據(jù)的恢復(fù),但二者是有著根本的不同的:

(1)作用不同:redo log是用于crash recovery的,保證MySQL宕機(jī)也不會(huì)影響持久性;binlog是用于point-in-time recovery的,保證服務(wù)器可以基于時(shí)間點(diǎn)恢復(fù)數(shù)據(jù),此外binlog還用于主從復(fù)制。

(2)層次不同:redo log是InnoDB存儲(chǔ)引擎實(shí)現(xiàn)的,而binlog是MySQL的服務(wù)器層(可以參考文章前面對MySQL邏輯架構(gòu)的介紹)實(shí)現(xiàn)的,同時(shí)支持InnoDB和其他存儲(chǔ)引擎。

(3)內(nèi)容不同:redo log是物理日志,內(nèi)容基于磁盤的Page;binlog的內(nèi)容是二進(jìn)制的,根據(jù)binlog_format參數(shù)的不同,可能基于sql語句、基于數(shù)據(jù)本身或者二者的混合。

(4)寫入時(shí)機(jī)不同:binlog在事務(wù)提交時(shí)寫入;redo log的寫入時(shí)機(jī)相對多元:

  • 前面曾提到:當(dāng)事務(wù)提交時(shí)會(huì)調(diào)用fsync對redo log進(jìn)行刷盤;這是默認(rèn)情況下的策略,修改innodb_flush_log_at_trx_commit參數(shù)可以改變該策略,但事務(wù)的持久性將無法保證。

  • 除了事務(wù)提交時(shí),還有其他刷盤時(shí)機(jī):如master thread每秒刷盤一次redo log等,這樣的好處是不一定要等到commit時(shí)刷盤,commit速度大大加快。

四、隔離性

1. 定義

與原子性、持久性側(cè)重于研究事務(wù)本身不同,隔離性研究的是不同事務(wù)之間的相互影響。隔離性是指,事務(wù)內(nèi)部的操作與其他事務(wù)是隔離的,并發(fā)執(zhí)行的各個(gè)事務(wù)之間不能互相干擾。嚴(yán)格的隔離性,對應(yīng)了事務(wù)隔離級別中的Serializable (可串行化),但實(shí)際應(yīng)用中出于性能方面的考慮很少會(huì)使用可串行化。

隔離性追求的是并發(fā)情形下事務(wù)之間互不干擾。簡單起見,我們主要考慮最簡單的讀操作和寫操作(加鎖讀等特殊讀操作會(huì)特殊說明),那么隔離性的探討,主要可以分為兩個(gè)方面:

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

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

2. 鎖機(jī)制

首先來看兩個(gè)事務(wù)的寫操作之間的相互影響。隔離性要求同一時(shí)刻只能有一個(gè)事務(wù)對數(shù)據(jù)進(jìn)行寫操作,InnoDB通過鎖機(jī)制來保證這一點(diǎn)。

鎖機(jī)制的基本原理可以概括為:事務(wù)在修改數(shù)據(jù)之前,需要先獲得相應(yīng)的鎖;獲得鎖之后,事務(wù)便可以修改數(shù)據(jù);該事務(wù)操作期間,這部分?jǐn)?shù)據(jù)是鎖定的,其他事務(wù)如果需要修改數(shù)據(jù),需要等待當(dāng)前事務(wù)提交或回滾后釋放鎖。

行鎖與表鎖

按照粒度,鎖可以分為表鎖、行鎖以及其他位于二者之間的鎖。表鎖在操作數(shù)據(jù)時(shí)會(huì)鎖定整張表,并發(fā)性能較差;行鎖則只鎖定需要操作的數(shù)據(jù),并發(fā)性能好。但是由于加鎖本身需要消耗資源(獲得鎖、檢查鎖、釋放鎖等都需要消耗資源),因此在鎖定數(shù)據(jù)較多情況下使用表鎖可以節(jié)省大量資源。MySQL中不同的存儲(chǔ)引擎支持的鎖是不一樣的,例如MyIsam只支持表鎖,而InnoDB同時(shí)支持表鎖和行鎖,且出于性能考慮,絕大多數(shù)情況下使用的都是行鎖。

如何查看鎖信息

有多種方法可以查看InnoDB中鎖的情況,例如:

select * from information_schema.innodb_locks; #鎖的概況
show engine innodb status; #InnoDB整體狀態(tài),其中包括鎖的情況

下面來看一個(gè)例子:

#在事務(wù)A中執(zhí)行:
start transaction;
update account SET balance = 1000 where id = 1;
#在事務(wù)B中執(zhí)行:
start transaction;
update account SET balance = 2000 where id = 1;

此時(shí)查看鎖的情況:

mysql事務(wù)指的是什么

show engine innodb status查看鎖相關(guān)的部分:

mysql事務(wù)指的是什么

通過上述命令可以查看事務(wù)24052和24053占用鎖的情況;其中l(wèi)ock_type為RECORD,代表鎖為行鎖(記錄鎖);lock_mode為X,代表排它鎖(寫鎖)。

除了排它鎖(寫鎖)之外,MySQL中還有共享鎖(讀鎖)的概念。由于本文重點(diǎn)是MySQL事務(wù)的實(shí)現(xiàn)原理,因此對鎖的介紹到此為止,后續(xù)會(huì)專門寫文章分析MySQL中不同鎖的區(qū)別、使用場景等,歡迎關(guān)注。

介紹完寫操作之間的相互影響,下面討論寫操作對讀操作的影響。

3. 臟讀、不可重復(fù)讀和幻讀

首先來看并發(fā)情況下,讀操作可能存在的三類問題:

(1)臟讀:當(dāng)前事務(wù)(A)中可以讀到其他事務(wù)(B)未提交的數(shù)據(jù)(臟數(shù)據(jù)),這種現(xiàn)象是臟讀。舉例如下(以賬戶余額表為例):

mysql事務(wù)指的是什么

(2)不可重復(fù)讀:在事務(wù)A中先后兩次讀取同一個(gè)數(shù)據(jù),兩次讀取的結(jié)果不一樣,這種現(xiàn)象稱為不可重復(fù)讀。臟讀與不可重復(fù)讀的區(qū)別在于:前者讀到的是其他事務(wù)未提交的數(shù)據(jù),后者讀到的是其他事務(wù)已提交的數(shù)據(jù)。舉例如下:

mysql事務(wù)指的是什么

(3)幻讀:在事務(wù)A中按照某個(gè)條件先后兩次查詢數(shù)據(jù)庫,兩次查詢結(jié)果的條數(shù)不同,這種現(xiàn)象稱為幻讀。不可重復(fù)讀與幻讀的區(qū)別可以通俗的理解為:前者是數(shù)據(jù)變了,后者是數(shù)據(jù)的行數(shù)變了。舉例如下:

mysql事務(wù)指的是什么

4. 事務(wù)隔離級別

SQL標(biāo)準(zhǔn)中定義了四種隔離級別,并規(guī)定了每種隔離級別下上述幾個(gè)問題是否存在。一般來說,隔離級別越低,系統(tǒng)開銷越低,可支持的并發(fā)越高,但隔離性也越差。隔離級別與讀問題的關(guān)系如下:

mysql事務(wù)指的是什么

在實(shí)際應(yīng)用中,讀未提交在并發(fā)時(shí)會(huì)導(dǎo)致很多問題,而性能相對于其他隔離級別提高卻很有限,因此使用較少。可串行化強(qiáng)制事務(wù)串行,并發(fā)效率很低,只有當(dāng)對數(shù)據(jù)一致性要求極高且可以接受沒有并發(fā)時(shí)使用,因此使用也較少。因此在大多數(shù)數(shù)據(jù)庫系統(tǒng)中,默認(rèn)的隔離級別是讀已提交(如Oracle)可重復(fù)讀(后文簡稱RR。

可以通過如下兩個(gè)命令分別查看全局隔離級別和本次會(huì)話的隔離級別:

mysql事務(wù)指的是什么

mysql事務(wù)指的是什么

InnoDB默認(rèn)的隔離級別是RR,后文會(huì)重點(diǎn)介紹RR。需要注意的是,在SQL標(biāo)準(zhǔn)中,RR是無法避免幻讀問題的,但是InnoDB實(shí)現(xiàn)的RR避免了幻讀問題。

5. MVCC

RR解決臟讀、不可重復(fù)讀、幻讀等問題,使用的是MVCC:MVCC全稱Multi-Version Concurrency Control,即多版本的并發(fā)控制協(xié)議。下面的例子很好的體現(xiàn)了MVCC的特點(diǎn):在同一時(shí)刻,不同的事務(wù)讀取到的數(shù)據(jù)可能是不同的(即多版本)——在T5時(shí)刻,事務(wù)A和事務(wù)C可以讀取到不同版本的數(shù)據(jù)。

mysql事務(wù)指的是什么

MVCC最大的優(yōu)點(diǎn)是讀不加鎖,因此讀寫不沖突,并發(fā)性能好。InnoDB實(shí)現(xiàn)MVCC,多個(gè)版本的數(shù)據(jù)可以共存,主要基于以下技術(shù)及數(shù)據(jù)結(jié)構(gòu):

1)隱藏列:InnoDB中每行數(shù)據(jù)都有隱藏列,隱藏列中包含了本行數(shù)據(jù)的事務(wù)id、指向undo log的指針等。

2)基于undo log的版本鏈:前面說到每行數(shù)據(jù)的隱藏列中包含了指向undo log的指針,而每條undo log也會(huì)指向更早版本的undo log,從而形成一條版本鏈。

3)ReadView:通過隱藏列和版本鏈,MySQL可以將數(shù)據(jù)恢復(fù)到指定版本;但是具體要恢復(fù)到哪個(gè)版本,則需要根據(jù)ReadView來確定。所謂ReadView,是指事務(wù)(記做事務(wù)A)在某一時(shí)刻給整個(gè)事務(wù)系統(tǒng)(trx_sys)打快照,之后再進(jìn)行讀操作時(shí),會(huì)將讀取到的數(shù)據(jù)中的事務(wù)id與trx_sys快照比較,從而判斷數(shù)據(jù)對該ReadView是否可見,即對事務(wù)A是否可見。

trx_sys中的主要內(nèi)容,以及判斷可見性的方法如下:

  • low_limit_id:表示生成ReadView時(shí)系統(tǒng)中應(yīng)該分配給下一個(gè)事務(wù)的id。如果數(shù)據(jù)的事務(wù)id大于等于low_limit_id,則對該ReadView不可見。

  • up_limit_id:表示生成ReadView時(shí)當(dāng)前系統(tǒng)中活躍的讀寫事務(wù)中最小的事務(wù)id。如果數(shù)據(jù)的事務(wù)id小于up_limit_id,則對該ReadView可見。

  • rw_trx_ids:表示生成ReadView時(shí)當(dāng)前系統(tǒng)中活躍的讀寫事務(wù)的事務(wù)id列表。如果數(shù)據(jù)的事務(wù)id在low_limit_id和up_limit_id之間,則需要判斷事務(wù)id是否在rw_trx_ids中:如果在,說明生成ReadView時(shí)事務(wù)仍在活躍中,因此數(shù)據(jù)對ReadView不可見;如果不在,說明生成ReadView時(shí)事務(wù)已經(jīng)提交了,因此數(shù)據(jù)對ReadView可見。

下面以RR隔離級別為例,結(jié)合前文提到的幾個(gè)問題分別說明。

(1)臟讀

mysql事務(wù)指的是什么

當(dāng)事務(wù)A在T3時(shí)刻讀取zhangsan的余額前,會(huì)生成ReadView,由于此時(shí)事務(wù)B沒有提交仍然活躍,因此其事務(wù)id一定在ReadView的rw_trx_ids中,因此根據(jù)前面介紹的規(guī)則,事務(wù)B的修改對ReadView不可見。接下來,事務(wù)A根據(jù)指針指向的undo log查詢上一版本的數(shù)據(jù),得到zhangsan的余額為100。這樣事務(wù)A就避免了臟讀。

(2)不可重復(fù)讀

mysql事務(wù)指的是什么

當(dāng)事務(wù)A在T2時(shí)刻讀取zhangsan的余額前,會(huì)生成ReadView。此時(shí)事務(wù)B分兩種情況討論,一種是如圖中所示,事務(wù)已經(jīng)開始但沒有提交,此時(shí)其事務(wù)id在ReadView的rw_trx_ids中;一種是事務(wù)B還沒有開始,此時(shí)其事務(wù)id大于等于ReadView的low_limit_id。無論是哪種情況,根據(jù)前面介紹的規(guī)則,事務(wù)B的修改對ReadView都不可見。

當(dāng)事務(wù)A在T5時(shí)刻再次讀取zhangsan的余額時(shí),會(huì)根據(jù)T2時(shí)刻生成的ReadView對數(shù)據(jù)的可見性進(jìn)行判斷,從而判斷出事務(wù)B的修改不可見;因此事務(wù)A根據(jù)指針指向的undo log查詢上一版本的數(shù)據(jù),得到zhangsan的余額為100,從而避免了不可重復(fù)讀。

(3)幻讀

mysql事務(wù)指的是什么

MVCC避免幻讀的機(jī)制與避免不可重復(fù)讀非常類似。

當(dāng)事務(wù)A在T2時(shí)刻讀取0<id<5的用戶余額前,會(huì)生成ReadView。此時(shí)事務(wù)B分兩種情況討論,一種是如圖中所示,事務(wù)已經(jīng)開始但沒有提交,此時(shí)其事務(wù)id在ReadView的rw_trx_ids中;一種是事務(wù)B還沒有開始,此時(shí)其事務(wù)id大于等于ReadView的low_limit_id。無論是哪種情況,根據(jù)前面介紹的規(guī)則,事務(wù)B的修改對ReadView都不可見。

當(dāng)事務(wù)A在T5時(shí)刻再次讀取0<id<5的用戶余額時(shí),會(huì)根據(jù)T2時(shí)刻生成的ReadView對數(shù)據(jù)的可見性進(jìn)行判斷,從而判斷出事務(wù)B的修改不可見。因此對于新插入的數(shù)據(jù)lisi(id=2),事務(wù)A根據(jù)其指針指向的undo log查詢上一版本的數(shù)據(jù),發(fā)現(xiàn)該數(shù)據(jù)并不存在,從而避免了幻讀。

擴(kuò)展

前面介紹的MVCC,是RR隔離級別下“非加鎖讀”實(shí)現(xiàn)隔離性的方式。下面是一些簡單的擴(kuò)展。

(1)讀已提交(RC)隔離級別下的非加鎖讀

RC與RR一樣,都使用了MVCC,其主要區(qū)別在于:

RR是在事務(wù)開始后第一次執(zhí)行select前創(chuàng)建ReadView,直到事務(wù)提交都不會(huì)再創(chuàng)建。根據(jù)前面的介紹,RR可以避免臟讀、不可重復(fù)讀和幻讀。

RC每次執(zhí)行select前都會(huì)重新建立一個(gè)新的ReadView,因此如果事務(wù)A第一次select之后,事務(wù)B對數(shù)據(jù)進(jìn)行了修改并提交,那么事務(wù)A第二次select時(shí)會(huì)重新建立新的ReadView,因此事務(wù)B的修改對事務(wù)A是可見的。因此RC隔離級別可以避免臟讀,但是無法避免不可重復(fù)讀和幻讀。

(2)加鎖讀與next-key lock

按照是否加鎖,MySQL的讀可以分為兩種:

一種是非加鎖讀,也稱作快照讀、一致性讀,使用普通的select語句,這種情況下使用MVCC避免了臟讀、不可重復(fù)讀、幻讀,保證了隔離性。

另一種是加鎖讀,查詢語句有所不同,如下所示:

#共享鎖讀取
select...lock in share mode
#排它鎖讀取
select...for update

加鎖讀在查詢時(shí)會(huì)對查詢的數(shù)據(jù)加鎖(共享鎖或排它鎖)。由于鎖的特性,當(dāng)某事務(wù)對數(shù)據(jù)進(jìn)行加鎖讀后,其他事務(wù)無法對數(shù)據(jù)進(jìn)行寫操作,因此可以避免臟讀和不可重復(fù)讀。而避免幻讀,則需要通過next-key lock。next-key lock是行鎖的一種,實(shí)現(xiàn)相當(dāng)于record lock(記錄鎖) + gap lock(間隙鎖);其特點(diǎn)是不僅會(huì)鎖住記錄本身(record lock的功能),還會(huì)鎖定一個(gè)范圍(gap lock的功能)。因此,加鎖讀同樣可以避免臟讀、不可重復(fù)讀和幻讀,保證隔離性。

6. 總結(jié)

概括來說,InnoDB實(shí)現(xiàn)的RR,通過鎖機(jī)制(包含next-key lock)、MVCC(包括數(shù)據(jù)的隱藏列、基于undo log的版本鏈、ReadView)等,實(shí)現(xiàn)了一定程度的隔離性,可以滿足大多數(shù)場景的需要。

不過需要說明的是,RR雖然避免了幻讀問題,但是畢竟不是Serializable,不能保證完全的隔離,下面是兩個(gè)例子:

第一個(gè)例子,如果在事務(wù)中第一次讀取采用非加鎖讀,第二次讀取采用加鎖讀,則如果在兩次讀取之間數(shù)據(jù)發(fā)生了變化,兩次讀取到的結(jié)果不一樣,因?yàn)榧渔i讀時(shí)不會(huì)采用MVCC。

第二個(gè)例子,如下所示,大家可以自己驗(yàn)證一下。

mysql事務(wù)指的是什么

一致性

1. 基本概念

一致性是指事務(wù)執(zhí)行結(jié)束后,數(shù)據(jù)庫的完整性約束沒有被破壞,事務(wù)執(zhí)行的前后都是合法的數(shù)據(jù)狀態(tài)。數(shù)據(jù)庫的完整性約束包括但不限于:實(shí)體完整性(如行的主鍵存在且唯一)、列完整性(如字段的類型、大小、長度要符合要求)、外鍵約束、用戶自定義完整性(如轉(zhuǎn)賬前后,兩個(gè)賬戶余額的和應(yīng)該不變)。

2. 實(shí)現(xiàn)

可以說,一致性是事務(wù)追求的最終目標(biāo):前面提到的原子性、持久性和隔離性,都是為了保證數(shù)據(jù)庫狀態(tài)的一致性。此外,除了數(shù)據(jù)庫層面的保障,一致性的實(shí)現(xiàn)也需要應(yīng)用層面進(jìn)行保障。

實(shí)現(xiàn)一致性的措施包括:

  • 保證原子性、持久性和隔離性,如果這些特性無法保證,事務(wù)的一致性也無法保證

  • 數(shù)據(jù)庫本身提供保障,例如不允許向整形列插入字符串值、字符串長度不能超過列的限制等

  • 應(yīng)用層面進(jìn)行保障,例如如果轉(zhuǎn)賬操作只扣除轉(zhuǎn)賬者的余額,而沒有增加接收者的余額,無論數(shù)據(jù)庫實(shí)現(xiàn)的多么完美,也無法保證狀態(tài)的一致

讀到這里,這篇“mysql事務(wù)指的是什么”文章已經(jīng)介紹完畢,想要掌握這篇文章的知識(shí)點(diǎn)還需要大家自己動(dòng)手實(shí)踐使用過才能領(lǐng)會(huì),如果想了解更多相關(guān)內(nèi)容的文章,歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI