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