InnoDB引擎中的事務(wù)詳解
一、事務(wù)
事務(wù)(Transaction)是訪(fǎng)問(wèn)和更新數(shù)據(jù)庫(kù)的程序執(zhí)行單元;事務(wù)中可能包含一個(gè)或多個(gè)sql語(yǔ)句,這些語(yǔ)句要么都執(zhí)行,要么都不執(zhí)行。MySQL中默認(rèn)采用的是自動(dòng)提交(autocommit),在自動(dòng)提交模式下,如果沒(méi)有start transaction顯式地開(kāi)始一個(gè)事務(wù),那么每個(gè)sql語(yǔ)句都會(huì)被當(dāng)做一個(gè)事務(wù)執(zhí)行提交操作。
start transaction; #1.開(kāi)始事務(wù) …… #一條或多條sql語(yǔ)句 commit; #2.提交事務(wù)
1.自動(dòng)提交(autocommit)
可以關(guān)閉autocommit(set autocommit = 0);需要注意的是,autocommit參數(shù)是針對(duì)連接的,在一個(gè)連接中修改了參數(shù),不會(huì)對(duì)其他連接產(chǎn)生影響。如果關(guān)閉了autocommit,則所有的sql語(yǔ)句都在一個(gè)事務(wù)中,直到執(zhí)行了commit或rollback,該事務(wù)結(jié)束,同時(shí)開(kāi)始了另外一個(gè)事務(wù)。
2. 特殊操作
在MySQL中,存在一些特殊的命令,如果在事務(wù)中執(zhí)行了這些命令,會(huì)馬上強(qiáng)制執(zhí)行commit提交事務(wù);如DDL語(yǔ)句(create table/drop table/alter/table)、lock tables語(yǔ)句等等。不過(guò),常用的select、insert、update和delete命令,都不會(huì)強(qiáng)制提交事務(wù)。
二、事務(wù)的ACID特性
1.原子性(Atomicity):
1.1.定義
指的是整個(gè)事務(wù)要么全部成功,要么全部失敗,如果事務(wù)中一個(gè)sql語(yǔ)句執(zhí)行失敗,則已執(zhí)行的語(yǔ)句rollback也必須回滾,數(shù)據(jù)庫(kù)退回到事務(wù)前的狀態(tài)。
1.2.實(shí)現(xiàn)原理:undo log
InnoDB通過(guò)undolog保證rollback的時(shí)候能找到之前的數(shù)據(jù)。生成的undo log中會(huì)包含被修改行的主鍵(知道修改了哪些行)、修改了哪些列、這些列在修改前后的值等信息。當(dāng)事務(wù)回滾時(shí)能夠撤銷(xiāo)所有已經(jīng)成功執(zhí)行的sql語(yǔ)句。InnoDB實(shí)現(xiàn)回滾,靠的是undo log:當(dāng)事務(wù)對(duì)數(shù)據(jù)庫(kù)進(jìn)行修改時(shí),InnoDB會(huì)生成對(duì)應(yīng)的undo log;如果事務(wù)執(zhí)行失敗或調(diào)用了rollback,導(dǎo)致事務(wù)需要回滾,便可以利用undo log中的信息將數(shù)據(jù)回滾到修改之前的樣子。對(duì)于每個(gè)insert,回滾時(shí)會(huì)執(zhí)行delete;對(duì)于每個(gè)delete,回滾時(shí)會(huì)執(zhí)行insert;對(duì)于每個(gè)update,回滾時(shí)會(huì)執(zhí)行一個(gè)相反的update,把數(shù)據(jù)改回去。
2.一致性(Consistency):
2.1 定義:
一致性是指事務(wù)執(zhí)行結(jié)束后,數(shù)據(jù)庫(kù)的完整性約束沒(méi)有被破壞,事務(wù)執(zhí)行的前后都是合法的數(shù)據(jù)狀態(tài)
2.2 實(shí)現(xiàn)原理
主要通過(guò)crash recovery和double write buffer的機(jī)制保證數(shù)據(jù)的一致性。
3.隔離性(Isolation):
3.1 定義:
隔離性是指,事務(wù)內(nèi)部的操作與其他事務(wù)是隔離的,并發(fā)執(zhí)行的各個(gè)事務(wù)之間不能互相干擾。隔離是不同事務(wù)之間的相互影響。
(1)隔離級(jí)別:
讀未提交在并發(fā)時(shí)會(huì)導(dǎo)致很多問(wèn)題,而性能相對(duì)于其他隔離級(jí)別提高卻很有限,因此使用較少??纱谢瘡?qiáng)制事務(wù)串行,并發(fā)效率很低,因此使用也較少。因此在大多數(shù)數(shù)據(jù)庫(kù)系統(tǒng)中,默認(rèn)的隔離級(jí)別是讀已提交(如Oracle)或可重復(fù)讀(InnoDB)(后文簡(jiǎn)稱(chēng)RR),可分全局隔離級(jí)別和本次會(huì)話(huà)的隔離級(jí)別。
(2)隔離性可以分為兩個(gè)方面:
- (一個(gè)事務(wù))寫(xiě)操作對(duì)(另一個(gè)事務(wù))寫(xiě)操作的影響:鎖機(jī)制保證 (寫(xiě)與寫(xiě))隔離性
- (一個(gè)事務(wù))寫(xiě)操作對(duì)(另一個(gè)事務(wù))讀操作的影響:MVCC保證 (寫(xiě)與讀)隔離性
隔離性要求同一時(shí)刻只能有一個(gè)事務(wù)對(duì)數(shù)據(jù)進(jìn)行寫(xiě)操作,InnoDB通過(guò)鎖機(jī)制來(lái)保證這一點(diǎn)。行鎖則只鎖定需要操作的數(shù)據(jù),但是由于加鎖本身需要消耗資源(獲得鎖、檢查鎖、釋放鎖等都需要消耗資源),因此在鎖定數(shù)據(jù)較多情況下使用表鎖可以節(jié)省大量資源。RR是無(wú)法避免幻讀問(wèn)題的,所以InnoDB引入MVVC實(shí)現(xiàn)的RR避免了幻讀問(wèn)題。
3.2臟讀、不可重復(fù)讀和幻讀
(1)臟讀:
當(dāng)前事務(wù)(A)中可以讀到其他事務(wù)(B)未提交的數(shù)據(jù)(臟數(shù)據(jù)),這種現(xiàn)象是臟讀。
(2)不可重復(fù)讀:
在事務(wù)A中先后兩次讀取同一個(gè)數(shù)據(jù),兩次讀取的結(jié)果不一樣,這種現(xiàn)象稱(chēng)為不可重復(fù)讀。臟讀與不可重復(fù)讀的區(qū)別在于:前者讀到的是其他事務(wù)未提交的數(shù)據(jù),后者讀到的是其他事務(wù)已提交的數(shù)據(jù)。
(3)幻讀:
在事務(wù)A中按照某個(gè)條件先后兩次查詢(xún)數(shù)據(jù)庫(kù),兩次查詢(xún)結(jié)果的條數(shù)不同,這種現(xiàn)象稱(chēng)為幻讀。不可重復(fù)讀與幻讀的區(qū)別可以通俗的理解為:前者是數(shù)據(jù)變了,后者是數(shù)據(jù)的行數(shù)變了。
3.3 MVVC多版本的并發(fā)控制
MVCC的特點(diǎn):在同一時(shí)刻,不同的事務(wù)讀取到的數(shù)據(jù)可能是不同的(即多版本),事務(wù)A和事務(wù)C可以讀取到不同版本的數(shù)據(jù)。
MVCC最大的優(yōu)點(diǎn)是讀不加鎖,因此讀寫(xiě)不沖突,多個(gè)版本的數(shù)據(jù)可以共存,主要基于以下技術(shù)及數(shù)據(jù)結(jié)構(gòu):
1)隱藏列:InnoDB中每行數(shù)據(jù)都有隱藏列,隱藏列中包含了本行數(shù)據(jù)的事務(wù)id、指向undo log的指針等。
2)基于undo log的版本鏈:前面說(shuō)到每行數(shù)據(jù)的隱藏列中包含了指向undo log的指針,而每條undo log也會(huì)指向更早版本的undo log,從而形成一條版本鏈。
3)ReadView:指事務(wù)(記做事務(wù)A)在某一時(shí)刻給整個(gè)事務(wù)系統(tǒng)(trx_sys)打快照,之后再進(jìn)行讀操作時(shí),會(huì)將讀取到的數(shù)據(jù)中的事務(wù)id與trx_sys快照比較,從而判斷數(shù)據(jù)對(duì)該ReadView是否可見(jiàn),即對(duì)事務(wù)A是否可見(jiàn)。
trx_sys中的主要內(nèi)容,以及判斷可見(jiàn)性的方法如下:
- low_limit_id:表示生成ReadView時(shí)系統(tǒng)中應(yīng)該分配給下一個(gè)事務(wù)的id。如果數(shù)據(jù)的事務(wù)id大于等于low_limit_id,則對(duì)該ReadView不可見(jiàn)。
- up_limit_id:表示生成ReadView時(shí)當(dāng)前系統(tǒng)中活躍的讀寫(xiě)事務(wù)中最小的事務(wù)id。如果數(shù)據(jù)的事務(wù)id小于up_limit_id,則對(duì)該ReadView可見(jiàn)。
- rw_trx_ids:表示生成ReadView時(shí)當(dāng)前系統(tǒng)中活躍的讀寫(xiě)事務(wù)的事務(wù)id列表。如果數(shù)據(jù)的事務(wù)id在low_limit_id和up_limit_id之間,則需要判斷事務(wù)id是否在rw_trx_ids中:如果在,說(shuō)明生成ReadView時(shí)事務(wù)仍在活躍中,因此數(shù)據(jù)對(duì)ReadView不可見(jiàn);如果不在,說(shuō)明生成ReadView時(shí)事務(wù)已經(jīng)提交了,因此數(shù)據(jù)對(duì)ReadView可見(jiàn)。
3.4. 鎖機(jī)制
兩個(gè)事務(wù)的寫(xiě)操作之間的相互影響。隔離性要求同一時(shí)刻只能有一個(gè)事務(wù)對(duì)數(shù)據(jù)進(jìn)行寫(xiě)操作,InnoDB通過(guò)鎖機(jī)制來(lái)保證這一點(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ā)性能好。MyIsam只支持表鎖,而InnoDB同時(shí)支持表鎖和行鎖,且出于性能考慮,絕大多數(shù)情況下使用的都是行鎖。
如何查看鎖信息
有多種方法可以查看InnoDB中鎖的情況,例如:
select * from information_schema.innodb_locks; #鎖的概況 show engine innodb status; #InnoDB整體狀態(tài),其中包括鎖的情況
下面來(lá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í)查看鎖的情況:
show engine innodb status查看鎖相關(guān)的部分:
通過(guò)上述命令可以查看事務(wù)24052和24053占用鎖的情況;其中l(wèi)ock_type為RECORD,代表鎖為行鎖(記錄鎖);lock_mode為X,代表排它鎖(寫(xiě)鎖)。
4.持久性(Durability):
4.1 定義:
數(shù)據(jù)在事務(wù)commit 后在任何情況下都不能丟。InnoDB通過(guò)redolog保證已經(jīng)commit的數(shù)據(jù)一定不會(huì)丟失。
4.2實(shí)現(xiàn)原理:redo log
redo log存在背景:
InnoDB Buffer Pool中包含了磁盤(pán)中部分?jǐn)?shù)據(jù)頁(yè)的映射,當(dāng)向數(shù)據(jù)庫(kù)寫(xiě)入數(shù)據(jù)時(shí),會(huì)首先寫(xiě)入Buffer Pool。Buffer Pool中修改的數(shù)據(jù)會(huì)定期刷新到磁盤(pán)中(這一過(guò)程稱(chēng)為刷臟)。但是也帶了新的問(wèn)題:如果MySQL宕機(jī),而此時(shí)Buffer Pool中修改的數(shù)據(jù)還沒(méi)有刷新到磁盤(pán),就會(huì)導(dǎo)致數(shù)據(jù)的丟失,事務(wù)的持久性無(wú)法保證。
redo log被引入來(lái)解決這個(gè)問(wèn)題:當(dāng)數(shù)據(jù)修改時(shí),除了修改Buffer Pool中的數(shù)據(jù),還會(huì)在redo log記錄這次操作;當(dāng)事務(wù)提交時(shí),會(huì)調(diào)用fsync接口對(duì)redo log進(jìn)行刷盤(pán)。如果MySQL宕機(jī),重啟時(shí)可以讀取redo log中的數(shù)據(jù),對(duì)數(shù)據(jù)庫(kù)進(jìn)行恢復(fù)。redo log采用的是WAL(Write-ahead logging,預(yù)寫(xiě)式日志),所有修改先寫(xiě)入日志,再更新到Buffer Pool,保證了數(shù)據(jù)不會(huì)因MySQL宕機(jī)而丟失,從而滿(mǎn)足了持久性要求。
既然redo log也需要在事務(wù)提交時(shí)將日志寫(xiě)入磁盤(pán),為什么它比直接將Buffer Pool中修改的數(shù)據(jù)寫(xiě)入磁盤(pán)(即刷臟)要快呢?主要有以下兩方面的原因:
(1)刷臟是隨機(jī)IO,因?yàn)槊看涡薷牡臄?shù)據(jù)位置隨機(jī),但寫(xiě)redo log是追加操作,屬于順序IO。
(2)刷臟是以數(shù)據(jù)頁(yè)(Page)為單位的,MySQL默認(rèn)頁(yè)大小是16KB,一個(gè)Page上一個(gè)小修改都要整頁(yè)寫(xiě)入;而redo log中只包含真正需要寫(xiě)入的部分,無(wú)效IO大大減少。
到此這篇關(guān)于InnoDB引擎中的事務(wù)詳解的文章就介紹到這了,更多相關(guān)InnoDB事務(wù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MySQL 查詢(xún)某個(gè)字段不重復(fù)的所有記錄
現(xiàn)在想從這5條記錄中查詢(xún)所有title不重復(fù)的記錄2009-05-05Windows10下mysql 8.0.16 安裝配置方法圖文教程
這篇文章主要為大家詳細(xì)介紹了Windows10下mysql 8.0.16 安裝配置方法圖文教程,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-05-05Windows重啟MySQL數(shù)據(jù)庫(kù)的多種方式
這篇文章主要介紹了Windows重啟MySQL數(shù)據(jù)庫(kù)的多種方式,在Windows上重啟MySQL服務(wù)可通過(guò)命令行、服務(wù)管理控制臺(tái)或MySQLWorkbench進(jìn)行,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2025-03-03MySQL定時(shí)全庫(kù)備份數(shù)據(jù)庫(kù)
數(shù)據(jù)備份真的很重要, 因?yàn)榭赡苡幸惶鞌?shù)據(jù)會(huì)被莫名其妙的刪掉了,本文主要介紹了MySQL定時(shí)備份數(shù)據(jù)庫(kù),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-07-07mysql decimal數(shù)據(jù)類(lèi)型轉(zhuǎn)換的實(shí)現(xiàn)
這篇文章主要介紹了mysql decimal數(shù)據(jù)類(lèi)型轉(zhuǎn)換的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-02-02