說起MySQL事務(wù)處理的四大特性,相信大家都可以張口就來:ACID!
那 MySQL是如何實(shí)現(xiàn)ACID的?每種特性的原理又是如何實(shí)現(xiàn)的?
今天,本文筆者主要探討MYSQL InnoDB引擎下的ACID實(shí)現(xiàn)原理,對(duì)事務(wù)、鎖以及隔離級(jí)別等內(nèi)容統(tǒng)一進(jìn)行回顧一下。
1、ACID特性
原子性(Atomicity)
單個(gè)事務(wù),為一個(gè)不可分割的最小工作單元,整個(gè)事務(wù)中的所有操作要么全部commit成功,要么全部失敗rollback,對(duì)于一個(gè)事務(wù)來說,不可能只執(zhí)行其中的一部分SQL操作,這就是事務(wù)的原子性。
一致性(Consistency)
數(shù)據(jù)庫(kù)總是從一個(gè)一致性的狀態(tài)轉(zhuǎn)換到另外一個(gè)一致性的狀態(tài)。在前面的例子中, 一致性確保了,即使在執(zhí)行第三、四條語(yǔ)句之間時(shí)系統(tǒng)崩潰,信用卡賬戶也不會(huì)損 失100塊,因?yàn)槭聞?wù)最終沒有提交,所以事務(wù)中所做的修改也不會(huì)保存到數(shù)據(jù)庫(kù)中,保證數(shù)據(jù)一致性。
隔離性(Isolation)
通常來說,一個(gè)事務(wù)所做的修改在最終提交以前,對(duì)其他事務(wù)是不可見(隔離)的。避免多個(gè)事務(wù)并發(fā)執(zhí)行的時(shí)候不會(huì)互相干擾。
持久性(Durability)
一旦事務(wù)提交,則其所做的修改就會(huì)永久保存到數(shù)據(jù)庫(kù)中,之后的其他操作或故障都不會(huì)對(duì)事務(wù)的結(jié)果產(chǎn)生影響。
2、ACID 具體實(shí)現(xiàn)
原子性:通過undolog來實(shí)現(xiàn)。
持久性:通過binlog、redolog來實(shí)現(xiàn)。
隔離性:通過(讀寫鎖+MVCC)來實(shí)現(xiàn)。
一致性:MySQL通過原子性、持久性、隔離性最終實(shí)現(xiàn)數(shù)據(jù)一致性。
對(duì)MySQL來說,邏輯備份日志(binlog)、重做日志(redolog)、回滾日志(undolog)、鎖技術(shù) + MVCC就是MySQL實(shí)現(xiàn)事務(wù)的基礎(chǔ)。
2.1 原子性原理
事務(wù)通常是以BEGIN TRANSACTION 開始,以 COMMIT 或 ROLLBACK 結(jié)束。
COMMIT 表示提交,即提交事務(wù)的所有操作并持久化到數(shù)據(jù)庫(kù)中。
ROLLBACK表示回滾,即在事務(wù)中運(yùn)行的過程中發(fā)生了某種故障,事務(wù)不能繼續(xù)執(zhí)行,系統(tǒng)將事務(wù)中對(duì)數(shù)據(jù)庫(kù)所有已完成的操作全部撤銷,回滾到事務(wù)開始時(shí)的狀態(tài),這里的操作指對(duì)數(shù)據(jù)庫(kù)的更新操作(查詢操作忽略)。這時(shí)候需要用到 undolog 來進(jìn)行回滾。
undolog:
每條數(shù)據(jù)變更(INSERT/UPDATE/DELETE/REPLACE)等操作都會(huì)生成一條undolog記錄,在SQL執(zhí)行前先于數(shù)據(jù)持久化到磁盤。
insert語(yǔ)句,回滾時(shí)會(huì)執(zhí)行 delete;
delete語(yǔ)句,回滾時(shí)會(huì)執(zhí)行insert;
update語(yǔ)句,回滾時(shí)便執(zhí)行相反的update,把數(shù)據(jù)改回來。
當(dāng)事務(wù)需要回滾時(shí),MySQL會(huì)根據(jù)回滾日志對(duì)事務(wù)中已執(zhí)行的SQL做逆向操作,比如 DELETE 一行數(shù)據(jù)的逆向操作就是再把這行數(shù)據(jù) INSERT回去,其他操作同理。
undolog記錄事務(wù)開始前老版本數(shù)據(jù),用于實(shí)現(xiàn)回滾,保證原子性,實(shí)現(xiàn)MVCC,會(huì)將數(shù)據(jù)修改前的舊版本保存在undolog,然后行記錄有個(gè)隱藏字段回滾指針指向老版本。
2.2 持久性原理
我們知道,MySQL表數(shù)據(jù)是持久化到磁盤中的,但如果所有操作都去操作磁盤,等并發(fā)上來了,那處理效率無法保證,因此引入了緩沖池(Buffer Pool)的概念,Buffer Pool 中包含了磁盤中部分?jǐn)?shù)據(jù)頁(yè)的映射,可以當(dāng)做緩存來用;這樣當(dāng)修改表數(shù)據(jù)時(shí),我們把操作記錄先寫到Buffer Pool中,并標(biāo)記事務(wù)已完成,等MySQL空閑時(shí),再把更新操作持久化到磁盤里,從而大大緩解了MySQL并發(fā)壓力。
MYSQL的持久性便是由redo log來保證。
redo log
是一種物理日志,作用:會(huì)記錄事務(wù)開啟后對(duì)數(shù)據(jù)做的修改,crash-safe。
它類似于一個(gè)卸貨的小推車,我們?nèi)羰敲啃兑患锲肪湍弥ト霂?kù),那豈不是特浪費(fèi)時(shí)間,若有一個(gè)小推車,我們將貨物首先存放在小推車,當(dāng)推車滿了再往庫(kù)里存,可以大大提升效率。
其實(shí)就是MySQL里經(jīng)常說到的WAL技術(shù),WAL的全稱是Write-Ahead Logging,它的關(guān)鍵點(diǎn)就是先寫日志,再寫磁盤,也就是先裝小推車,等不忙的時(shí)候再裝庫(kù)。
特性:空間一定,寫完后會(huì)循環(huán)寫,有兩個(gè)指針write pos指向當(dāng)前記錄位置,checkpoint指向?qū)⒉脸奈恢茫瑀edolog相當(dāng)于是個(gè)取貨小車,貨物太多時(shí)來不及一件一件入庫(kù)太慢了這樣,就先將貨物放入小車,等到貨物不多或則小車滿了或則店里空閑時(shí)再將小車貨物送到庫(kù)房。用于crash-safe,數(shù)據(jù)庫(kù)異常斷電等情況可用redo log恢復(fù)。
以下只作了解:
寫入流程:先寫redo log buffer,然后wite到文件系統(tǒng)的page cache,此時(shí)并沒有持久化,然后fsync持久化到磁盤
寫入策略:根據(jù)innodb_flush_log_at_trx_commit參數(shù)控制(innodb以事務(wù)的什么提交方式刷新日志)
0——>事務(wù)提交時(shí)只把redo log留在redo log buffer
1——>將redo log直接持久化到磁盤(所以有個(gè)雙“1”配置,后面會(huì)講)
2——>只是把redo log寫到page cache
2.3 隔離性原理
MYSQL有四種隔離級(jí)別,用來解決存在的并發(fā)問題:臟讀、幻讀、不可重復(fù)讀。
那么不同隔離級(jí)別,隔離性是怎樣實(shí)現(xiàn)的呢?
一句話:鎖+MVCC。
鎖
表鎖:讀鎖(不會(huì)阻塞其他線程的讀操作,阻塞寫操作);寫鎖(讀寫操作都阻塞)
行鎖:需要的時(shí)候加上,并不是馬上釋放,等事務(wù)提交才釋放,兩階段鎖協(xié)議
鎖的類型
間隙鎖-gap lock:鎖定區(qū)間范圍,防止幻讀,左開右開,只在可重復(fù)讀隔離級(jí)別下生效—|—為了阻止多個(gè)事務(wù)將記錄插入到同一范圍內(nèi),而這會(huì)導(dǎo)致幻讀問題的產(chǎn)生
記錄鎖-record Lock:鎖定行記錄,索的索引,索引失效,為表鎖
臨鍵鎖-next-key Lock:record lock+gap lock 左開右閉(解決幻讀)
鎖的模式
select .... for update (持有寫鎖,別的不可加讀鎖,也不可加寫鎖)
select .... lock in share mode(持有讀鎖,別的可以再加讀鎖,不可加寫鎖)
共享鎖-讀鎖-S鎖
排他鎖-寫鎖-X鎖
意向鎖:讀意向鎖+寫意向鎖
自增鎖
全局鎖:全庫(kù)邏輯備份
死鎖:兩個(gè)或多個(gè)事務(wù)在同一資源上相互占用,并請(qǐng)求加鎖時(shí),造成相互等待,無限阻塞
MVCC:實(shí)現(xiàn)多版本并發(fā)控制,實(shí)現(xiàn)原理:使用版本鏈+Read View
讀已提交和可重復(fù)讀實(shí)現(xiàn)原理就是MVCC Read View不同的生成時(shí)機(jī)。可重復(fù)讀只在事務(wù)開始時(shí)生成一個(gè)Read View,之后都用的這個(gè);讀已提交每次執(zhí)行前都會(huì)生成Read View。
2.4 一致性原理
一致性是事務(wù)追求的最終目標(biāo),前文所述的原子性、持久性和隔離性,其實(shí)都是為了保證數(shù)據(jù)庫(kù)狀態(tài)的一致性,數(shù)據(jù)庫(kù)中的增刪改操作,使數(shù)據(jù)庫(kù)不斷從一個(gè)一致性的狀態(tài)轉(zhuǎn)移到另一個(gè)一致性的狀態(tài)。
總結(jié)
事務(wù)該回滾的回滾,該提交的提交,提交后該持久化磁盤的持久化磁盤,該寫緩沖池的寫緩沖池+寫日志。
對(duì)于數(shù)據(jù)可見性,通過四種隔離級(jí)別進(jìn)行控制,使得庫(kù)表中的有效數(shù)據(jù)范圍可控,保證業(yè)務(wù)數(shù)據(jù)的正確性的前提下,進(jìn)而提高并發(fā)程度,支撐服務(wù)高QPS的穩(wěn)定運(yùn)行,保證數(shù)據(jù)的一致性,這就是咱們說個(gè)不停的MySQL數(shù)據(jù)庫(kù)事務(wù)ACID四大特性。