Mysql中的事务详解
什么是事務(wù)
顧名思義,事務(wù)就是對一組事情的操作,要么把這件事辦成了,要么這事兒就失敗了;通俗來講,事務(wù)就是一組sql語句的集合,要么這組sql全都執(zhí)行成功,要么就全都執(zhí)行失敗;事務(wù)不是Mysql支持的,是InnoDB搜索引擎提供的。
描述事務(wù)最經(jīng)典的例子就是賬戶轉(zhuǎn)錢,A要給B轉(zhuǎn)1000元錢操作如下:
1、檢查A賬戶中余額是否高于1000元錢。
2、從A賬戶中減去1000元錢。
3、給B賬戶中增加1000元錢。
以上三個(gè)操作必須打包在一個(gè)事務(wù)當(dāng)中,該事務(wù)要么成功(A賬戶減少1000,B賬戶增加1000),要么失敗(A,B賬戶余額不變),不應(yīng)該出現(xiàn)第三種現(xiàn)象。
所以這也是引入事務(wù)的原因:事務(wù)會把數(shù)據(jù)庫從一種一致狀態(tài)轉(zhuǎn)換為另一種一致狀態(tài),在數(shù)據(jù)庫提交工作時(shí),可以確保要么所有修改都已經(jīng)保存了,要么所有修改都不會保存。
事務(wù)的特性
InnoDB存儲引擎中的事務(wù)完全符合ACID的特性,ACID是以下四個(gè)單詞的縮寫:
原子性(atomicity)
一致性(consistency)
隔離性(isolation)
持久性(durability)
原子性
原子是自然界非常小的單位,我們可以看成它是不可再分的,同時(shí)它也是事務(wù)的一個(gè)特征,任何一個(gè)事務(wù)都可以想象成一個(gè)原子,表示其不可再分。只有事務(wù)中所有的數(shù)據(jù)庫操作都執(zhí)行成功,才算整個(gè)事務(wù)成功,事務(wù)中任何一個(gè)sql語句執(zhí)行失敗,已經(jīng)執(zhí)行成功的sql語句也必須撤銷,數(shù)據(jù)庫狀態(tài)應(yīng)該退回到執(zhí)行事務(wù)前的狀態(tài)。
注意:如果事務(wù)中的操作都是只讀的,保持原子性比較簡單,一發(fā)生錯(cuò)誤,要么重試,要么返回錯(cuò)誤代碼即可,如果當(dāng)前事務(wù)中存在插入或者更新操作,一旦失敗,就會引起數(shù)據(jù)狀態(tài)的變化,因此要保護(hù)系統(tǒng)并發(fā)用戶訪問受影響的部分?jǐn)?shù)據(jù)。
一致性
指數(shù)據(jù)庫中數(shù)據(jù)在事務(wù)操作前和操作后都必須滿足業(yè)務(wù)規(guī)則約束,也就是A、B賬戶的總金額在轉(zhuǎn)賬前后必須一致,二者的總金額加起來不能多也不能少,如果有不一致,則必須是短暫的,且只有在事務(wù)提交前才會出現(xiàn)的。
再舉一個(gè)例子,在表中有一個(gè)字段為姓名,是唯一的約束,即在表中姓名不能重復(fù),如果有一個(gè)事務(wù)對姓名字段進(jìn)行了修改,在事務(wù)提交后,表中的姓名變得非唯一了,這就破壞了事務(wù)一致性的要求,因?yàn)槭聞?wù)將數(shù)據(jù)庫從一種狀態(tài)變成了一種不一致的狀態(tài)。
隔離性
隔離性還有其他稱呼,比如并發(fā)控制、可串行化、鎖等。通常來說,一個(gè)事務(wù)所做的修改在最終提交以前,對其他事務(wù)是不可見的。在轉(zhuǎn)賬的例子中,A向B轉(zhuǎn)賬時(shí),C同時(shí)向A轉(zhuǎn)賬,如果同時(shí)進(jìn)行,則A和B之間的一致行則不能滿足,所以,當(dāng)A和B執(zhí)行事務(wù)的過程中,其他事務(wù)是不能訪問或修改當(dāng)前相關(guān)的數(shù)值。
持久性
一旦事務(wù)提交,其所做的修改就會永久保存到數(shù)據(jù)庫中,此時(shí)即使系統(tǒng)崩潰,修改的數(shù)據(jù)也不會丟失。
注意:只能從事務(wù)本身的角度來保證結(jié)果是持久性,當(dāng)事務(wù)提交后,所有的變化都是永久的,即使數(shù)據(jù)庫崩潰需要恢復(fù)時(shí),也可以保證恢復(fù)后的數(shù)據(jù)都不會丟失,但是如果不是數(shù)據(jù)庫本身發(fā)生了問題,而是一些外部的原因,比如物理因素,自然災(zāi)害導(dǎo)致數(shù)據(jù)庫服務(wù)器爆炸,那所有的數(shù)據(jù)可能都會丟失,因此持久性保證系統(tǒng)的高可靠性(High Reliability),而不是高可用性(High Availability)。
可靠性:指的是服務(wù)連續(xù)無故障運(yùn)行的時(shí)間,無故障運(yùn)行時(shí)間越長,可靠性就越高
可用性:在一段時(shí)間內(nèi),正常工作時(shí)間/這段時(shí)間=可用性。
一個(gè)服務(wù)器可以正常工作十年沒有問題,則它的可靠性為十年,但是如果服務(wù)器損壞,需要一年來維護(hù),則可用性就為90%。
事務(wù)的分類
從事務(wù)理論的角度來說,可以將事務(wù)分為以下幾種類型:
扁平事務(wù)(Flat Transactions)
帶有保存點(diǎn)的扁平事務(wù)(Flat Transactions With Savepoints)
鏈?zhǔn)聞?wù)(Chained Transactions)
嵌套事務(wù)(Nested Transactions)
分布式事務(wù)(Distributed Transactions)
扁平事務(wù)
扁平事務(wù)是事務(wù)類型中最簡單最常用的一種事務(wù),在扁平事務(wù)中,所有的操作都處于同一層次,事務(wù)開始到提交(回滾)之間操作是原子性的,要么都執(zhí)行,要么都回滾。扁平事務(wù)是應(yīng)用程序成為原子操作的基本組成模塊。下圖展示了扁平事務(wù)的三種結(jié)果:
扁平事務(wù)就是咱們平常說的事務(wù),如果執(zhí)行的操作有誤,就會全部回滾。現(xiàn)在假設(shè)當(dāng)前扁平事務(wù)有1000條sql,但執(zhí)行到最后一條sql時(shí)失敗了,這時(shí)就會全部回滾,即使前999條sql執(zhí)行成功也會進(jìn)行回滾,這樣代價(jià)太大了,于是就有了帶保存點(diǎn)的扁平事務(wù)。
帶有保存點(diǎn)的扁平事務(wù)
帶有保存點(diǎn)的扁平事務(wù)和扁平事務(wù)的區(qū)別就是有多個(gè)保存點(diǎn),保存點(diǎn)用來通知系統(tǒng)應(yīng)該記住事務(wù)當(dāng)前的狀態(tài),以便之后發(fā)生錯(cuò)誤時(shí),事務(wù)能回到保存點(diǎn)當(dāng)時(shí)的狀態(tài),這就解決了扁平事務(wù)全部回滾代價(jià)大的缺點(diǎn)。(為什么說這兩個(gè)事務(wù)的區(qū)別是帶有保存點(diǎn)的扁平事務(wù)有多個(gè)保存點(diǎn),其實(shí)扁平事務(wù)也隱式的設(shè)置了一個(gè)保存點(diǎn),這個(gè)保存點(diǎn)只存在于事務(wù)開始時(shí)的位置,如果出現(xiàn)問題,直接回到起點(diǎn))下面是帶有保存點(diǎn)的扁平事務(wù)使用:
上圖灰色部分表示已經(jīng)被回滾過的沒有執(zhí)行的事務(wù)。當(dāng)用BEGIN WORK 開啟一個(gè)事務(wù)的時(shí)候,隱式的包含了一個(gè)保存點(diǎn),當(dāng)事務(wù)通過ROLLBACK WORK:2 命令回滾時(shí),事務(wù)就會回到 SAVE WORK :2,然后繼續(xù)執(zhí)行,執(zhí)行到ROLLBACK WORK:7再次回滾,回滾到SAVE WORK 7 處,再繼續(xù)執(zhí)行,直到事務(wù)提交完畢,除了灰色部分,其他操作都已經(jīng)被提交。
注意:保存點(diǎn)是遞增的,執(zhí)行完了保存點(diǎn)2之后,下一個(gè)保存點(diǎn)是3,然后是4,即使3,4被回滾了,下一個(gè)保存點(diǎn)依然從4之后開始,也就是5,并不會重新從3開始。
鏈?zhǔn)聞?wù)
上面說到了帶有保存點(diǎn)的扁平事務(wù),在執(zhí)行該類型事務(wù)時(shí),如果系統(tǒng)發(fā)生了崩潰,則所有的保存點(diǎn)都會消失,因?yàn)楸4纥c(diǎn)是易失的(volatile),而非持久的(persistent),也就是當(dāng)進(jìn)行恢復(fù)時(shí),事務(wù)還是要從開始處重新執(zhí)行,而不會從最近的一個(gè)保存點(diǎn)繼續(xù)執(zhí)行。
于是就有了鏈?zhǔn)绞聞?wù):在提交一個(gè)事務(wù)時(shí),緊接著將上下文處理傳遞給下一個(gè)要開始的事務(wù),也就是當(dāng)前提交事務(wù)的操作和開始下一個(gè)事務(wù)的操作將合并成為一個(gè)原子操作。這意味著下一個(gè)事務(wù)將看到上一個(gè)事務(wù)的結(jié)果,就好像在一個(gè)事務(wù)中進(jìn)行的一樣。下圖展示了鏈?zhǔn)绞聞?wù)的工作方式:
鏈?zhǔn)绞聞?wù)與帶有保存點(diǎn)的扁平事務(wù)不同的是,帶有保存點(diǎn)的扁平事務(wù)可以回滾到任意正確的保存點(diǎn),而鏈?zhǔn)绞聞?wù)只能回滾到最近的一個(gè)保存點(diǎn),并且回滾只限于當(dāng)前事務(wù),就像是帶有保存點(diǎn)的扁平事務(wù)的當(dāng)前保存點(diǎn)之前,上一個(gè)保存點(diǎn)之后為一個(gè)事務(wù)。然后這些被分割的事務(wù)通過觸發(fā)器進(jìn)行連接成為鏈?zhǔn)聞?wù)。
嵌套事務(wù)
嵌套事務(wù)是一個(gè)層次結(jié)構(gòu)的框架,由一個(gè)頂層事務(wù)(top-level transaction)控制著各個(gè)層次的事務(wù),頂層事務(wù)之下嵌套的事務(wù)被成為子事務(wù)(subtransaction),其控制每一個(gè)局部的變化,嵌套事務(wù)結(jié)構(gòu)層次如下:
在嵌套事務(wù)中,實(shí)際的工作都是葉子節(jié)點(diǎn)來完成的,即只有葉子節(jié)點(diǎn)的事務(wù)才能訪問數(shù)據(jù)庫、發(fā)送消息、獲取其他類型的數(shù)據(jù)。高層事務(wù)僅負(fù)責(zé)邏輯控制,決定何時(shí)調(diào)用相關(guān)的的子事務(wù)。
對嵌套事務(wù)的定義:
1、嵌套事務(wù)是由若干事務(wù)組成的一棵樹,子事務(wù)既可以是嵌套事務(wù),也可以是扁平事務(wù)。
2、處在葉子節(jié)點(diǎn)的事務(wù)是扁平事務(wù),但是每個(gè)子事務(wù)從根到葉子節(jié)點(diǎn)的距離可以是不相同的。
3、位于根節(jié)點(diǎn)的事務(wù)成為頂層事務(wù),其他事務(wù)成為子事務(wù),事務(wù)的前驅(qū)(predecessor)為父事務(wù)(parent),事務(wù)的下一層稱為兒子事務(wù)(child)。
4、子事務(wù)既可以提交也可以回滾,但是它的提交操作并不馬上生效,除非其父事務(wù)已經(jīng)提交,所以子事務(wù)都是在頂層事務(wù)提交之后才會真正的提交。
5、樹中的任意一個(gè)事務(wù)的回滾會引起它的所有子事務(wù)一同回滾,故子事務(wù)僅保留ACI特性,不具有D的特性。
注意:對于InnoDB存儲引擎來說,是支持扁平事務(wù)、帶有保存點(diǎn)的扁平事務(wù)、鏈?zhǔn)绞聞?wù)、分布式事務(wù)的,并不原生支持嵌套事務(wù)。但可以通過帶有保存點(diǎn)的事務(wù)來模擬串行的嵌套事務(wù)。
分布式事務(wù)
分布式事務(wù)通常是一個(gè)在分布式環(huán)境下運(yùn)行的扁平事務(wù),因此需要根據(jù)數(shù)據(jù)所在位置訪問網(wǎng)絡(luò)中不同的節(jié)點(diǎn)。
還是拿上面轉(zhuǎn)賬的例子來說,用戶在ATM機(jī)處進(jìn)行轉(zhuǎn)賬操作,將招商銀行的錢轉(zhuǎn)賬到工商銀行,ATM機(jī)視為節(jié)點(diǎn)A,招商銀行后臺的數(shù)據(jù)庫視為節(jié)點(diǎn)B,工商銀行的后臺數(shù)據(jù)庫視為節(jié)點(diǎn)C。這里就需要使用分布式事務(wù),因?yàn)楣?jié)點(diǎn)A不能通過調(diào)用一臺數(shù)據(jù)庫就能完成任務(wù),需要訪問網(wǎng)絡(luò)中兩個(gè)節(jié)點(diǎn)的數(shù)據(jù)庫,每個(gè)節(jié)點(diǎn)的數(shù)據(jù)庫執(zhí)行的事務(wù)是扁平事務(wù)。
事務(wù)的實(shí)現(xiàn)
事務(wù)的隔離性通過鎖來實(shí)現(xiàn),原子性、一致性、持久性通過數(shù)據(jù)庫的redo log 和 undo log 來完成。redo log 稱為重做日志,用來保證事務(wù)的原子性和持久性,undo log 用來保證事務(wù)的一致性。
實(shí)現(xiàn)隔離性
隔離性是通過鎖來進(jìn)行實(shí)現(xiàn)的,參考博客:https://blog.csdn.net/aaaPostcard/article/details/118961910
實(shí)現(xiàn)原子性
要想保證事務(wù)的原子性,就需要在執(zhí)行發(fā)生異常時(shí),對已經(jīng)執(zhí)行的操作進(jìn)行回滾。在InnoDB中,恢復(fù)機(jī)制是通過回滾日志(undo log)來實(shí)現(xiàn)的,所有事務(wù)進(jìn)行的修改都會先記錄到這個(gè)回滾日志中。
那么是如何通過undo log來實(shí)現(xiàn)回滾的呢?當(dāng)我們對數(shù)據(jù)庫進(jìn)行修改時(shí),InnoDB存儲引擎不但會產(chǎn)生redo log 還會產(chǎn)生 undo log,如果用戶執(zhí)行的事務(wù)或者語句由于某種原因失敗了,可以利用ROLLBACK語句進(jìn)行回滾,這個(gè)回滾操作就是利用undo log中的信息,所以 undo log 中存放的是表之前的記錄。通過undo log 就可以保證該事務(wù)中某個(gè)操作失敗的話就全部回滾,使這整個(gè)事務(wù)失敗,來保證原子性。
回滾日志除了能夠在發(fā)生錯(cuò)誤或者用戶執(zhí)行 ROLLBACK 時(shí)提供回滾相關(guān)的信息,它還能夠在整個(gè)系統(tǒng)發(fā)生崩潰、數(shù)據(jù)庫進(jìn)程直接被殺死后,當(dāng)用戶再次啟動數(shù)據(jù)庫進(jìn)程時(shí),還能夠立刻通過查詢回滾日志將之前未完成的事務(wù)進(jìn)行回滾,這也就需要回滾日志必須先于數(shù)據(jù)持久化到磁盤上,是我們需要先寫日志后寫數(shù)據(jù)庫的主要原因。
undo log 簡介
undo log是邏輯日志,并不能將數(shù)據(jù)庫物理地恢復(fù)到執(zhí)行語句或事務(wù)之前的樣子,這是因?yàn)樵诙嘤脩舨l(fā)系統(tǒng)中,可能會有上千個(gè)并發(fā)事務(wù),如果一個(gè)事務(wù)在修改當(dāng)前一個(gè)頁中某幾條記錄,同時(shí)還有別的事務(wù)在對同一個(gè)頁中另幾條記錄進(jìn)行修改,就不能將這個(gè)頁回滾到事務(wù)開始的樣子,這樣會影響其他的事務(wù)。
例如:用戶執(zhí)行了一個(gè)INSERT 10w條記錄的事務(wù),這個(gè)事務(wù)會導(dǎo)致分配了一個(gè)新的段,即表空間會增大,當(dāng)再執(zhí)行ROLLBACK時(shí),會將插入的事務(wù)進(jìn)行回滾,但是表空間不可能再縮小,物理上不會改變。因此,當(dāng)InnoDB存儲引擎發(fā)生回滾時(shí),它實(shí)際做的是與先前相反的工作,對于每個(gè)INSERT,InnoDB存儲引擎會完成一個(gè)DELETE(只限于事務(wù)提交之前);對于DELETE,存儲引擎會執(zhí)行一個(gè)INSERT;對于每個(gè)UPDATE,InnoDB存儲引擎會執(zhí)行一個(gè)相反的UPDATE,將修改前的行放回去。最后一點(diǎn),undo log 會產(chǎn)生redo log,這是因?yàn)?undo log 也需要持久性的保護(hù)。除此之外 ,undo log 還可以實(shí)現(xiàn)非鎖定讀,詳細(xì)見上面實(shí)現(xiàn)隔離性中博客地址。
undo log 存儲管理
InnoDB對 undo 的管理采用段的方式,段存放于共享表空間中。InnoDB存儲引擎有rollback segment(回滾段),每個(gè)回滾段中記錄了1024個(gè)undo log segment(段),每一個(gè)undo log segment 代表一個(gè)事務(wù)。
在InnoDB1.1版本之前(不包括1.1版本),只有一個(gè)rollback segment,因此支持 1024個(gè)事務(wù),從1.1版本開始InnoDB支持最大128個(gè)rollback segment,也就是將同時(shí)在線的事務(wù)限制提高到了128 * 1024。
事務(wù)在undo log segment 分配頁并寫入undo log 的這個(gè)過程同樣需要寫入重做日志,當(dāng)事務(wù)提交時(shí),InnoDB存儲引擎會做兩件事情:
1、將undo log 放入列表中,以提供之后的purge 操作
2、判斷undo log 所在的頁是否可以重用,若可以分配給下個(gè)事務(wù)使用
事務(wù)提交后并不能馬上刪除 undo log 及undo log 所在的頁,這是因?yàn)榭赡苓€有其他事務(wù)需要通過undo log 來得到行記錄之前的版本(一致性非鎖定讀),所以事務(wù)提交時(shí)將undo log 放入一個(gè)鏈表中,是否可以最終刪除 undo log 及undo log 所在頁由purge 線程來判斷。
undo log 格式
在InnoDB存儲引擎中,undo log 分為:
1、insert undo log
2、update undo log
insert undo log 是指在insert 操作中產(chǎn)生的undo log,因?yàn)槭莍nsert操作的記錄,只對事務(wù)本身可見,對其他事務(wù)不可見(事務(wù)隔離性的要求),故該undo log 可以在事務(wù)提交后直接刪除,因?yàn)槠渌聞?wù)不可能讀到還未插入的數(shù)據(jù),這條數(shù)據(jù)根據(jù)就不存在。
上圖中*表示對存儲字段進(jìn)行了壓縮,next記錄的是下一個(gè)undo log 的位置,通過該next的字節(jié)可以知道一個(gè)undo log所占的空間字節(jié)數(shù);尾部的start記錄的是undo log 的開始的位置,占兩個(gè)字節(jié);type_cmpl占用一個(gè)字節(jié),記錄的是undo 的類型,對于 insert undo log,該值永遠(yuǎn)是11;undo no 記錄事務(wù)的ID;table_id記錄 undo log所對應(yīng)的表對象;剩下的部分記錄了所有主鍵的列和值,在rollback操作時(shí),根據(jù)這些值可以定位到具體的記錄,然后進(jìn)行直接刪除。
update undo log 記錄的是delete 和update 操作產(chǎn)生的undo log 可能需要提供MVCC 機(jī)制,因此不能在事務(wù)提交時(shí)就進(jìn)行刪除,提交時(shí)放入 undo log 鏈表,等待purge 線程進(jìn)行最后的刪除。update undo log 結(jié)構(gòu)如下:
next、start、undo_no、table_id與之前介紹的insert undo log 部分相同,update_vector 表示update操作導(dǎo)致發(fā)生改變的列。每個(gè)修改的列信息都要記錄到undo log中。
實(shí)現(xiàn)持久性
redo log 組成
實(shí)現(xiàn)持久性是通過redo log(重做日志)來實(shí)現(xiàn)的,其由兩個(gè)部分組成:一是內(nèi)存中的重做日志緩沖(redo log buffer),其是易失的;二是重做日志文件(redo log file),其是持久的。當(dāng)事務(wù)提交(COMMIT)時(shí),必須先將該事務(wù)的所有日志寫入到重做日志文件進(jìn)行持久化,待事務(wù)的COMMIT操作完成才算完成。
redo log 工作原理
在將redo log 寫入磁盤之前,先將重做日志緩沖寫入重做日志文件,然后再進(jìn)行一次fsync(同步文件到存儲設(shè)備)操作,才將日志寫入到磁盤當(dāng)中做持久化,由于fsync的效率取決于磁盤的性能,因此磁盤的性能決定了事務(wù)提交的性能,也就是數(shù)據(jù)庫的性能。如果在數(shù)據(jù)庫數(shù)據(jù)發(fā)生改變時(shí),直接修改到磁盤中,那整個(gè)過程的IO成本是非常高的,所以當(dāng)一條數(shù)據(jù)需要更新的時(shí)候,InnoDB先把記錄寫入到文件系統(tǒng)緩存,然后在適當(dāng)?shù)臅r(shí)候?qū)⒂涗浉碌酱疟P里面。
redo log 是固定大小的,比如可以配置一組4個(gè)文件,每個(gè)文件大小是1GB,總共就是4GB,從頭開始寫,寫到末尾就又回到開頭循環(huán)寫,如下圖所示:
write pos是當(dāng)前記錄的位置,一邊寫一邊后移,寫到第3號文件末尾后就回到0號文件開頭。checkpoint是當(dāng)前要擦除的位置,也是往后推移并且循環(huán)的,擦除記錄前要把記錄更新到數(shù)據(jù)文件。如果write pos追上checkpoint,表示日志滿了,這時(shí)候不能再執(zhí)行新的更新,得停下來先擦掉一些記錄,把checkpoint推進(jìn)一下。
調(diào)整redo log 同步機(jī)制
但是在redo log 寫入到磁盤之前,數(shù)據(jù)庫突然發(fā)生宕機(jī),由于部分日志未刷新到磁盤中,因此會丟失最后一段時(shí)間的事務(wù)。所以參數(shù) innodb_flush_log_at_trx_commit用來控制重做日志刷新到磁盤的策略。該參數(shù)默認(rèn)為1,表示事務(wù)提交時(shí)必須調(diào)用一次fsync操作,還可以設(shè)置參數(shù)0和2。0表示事務(wù)提交時(shí)不進(jìn)行寫入重做日志操作,該操作僅在master thread 中完成,而master thread 中每 1 秒進(jìn)行一次fsync操作。2表示事務(wù)提交時(shí)將重做日志寫入重做日志文件,但僅僅寫入文件系統(tǒng)緩存中,不進(jìn)行fsync操作,在這個(gè)設(shè)置下,當(dāng)mysql 數(shù)據(jù)庫發(fā)生宕機(jī)時(shí),并不會導(dǎo)致事務(wù)的丟失,當(dāng)操作系統(tǒng)發(fā)生宕機(jī)時(shí),重啟數(shù)據(jù)庫會丟失未從文件系統(tǒng)刷新到重做日志文件那部分事務(wù)。
打開mysql ,輸入命令,可看默認(rèn)是1:
更改命令:
不同的設(shè)置對插入50w行數(shù)據(jù)的速度如下:
redo log 與 bin log 區(qū)別
除了redo log之外,mysql中還有一種二進(jìn)制日志為歸檔日志bin log,bin log 是一個(gè)二進(jìn)制格式的文件,用于記錄用戶對數(shù)據(jù)庫更新的SQL語句信息,查詢的內(nèi)容不會進(jìn)行記錄。它與redo log 的區(qū)別如下:
1、redo log 是InnoDB引擎特有的,bin log 是Mysql 的Server層實(shí)現(xiàn)的,所有引擎都可以使用。
2、redo log 是物理日志記錄的是對每個(gè)頁的修改,bin log 和undo log 一樣,都是邏輯日志,記錄的是SQL語句。
3、redo log 是循環(huán)寫的,空間固定會用完;bin log 是可以追加寫入得,當(dāng)文件達(dá)到一定大小后悔切換到下一個(gè),并不會覆蓋以前的日志。
4、兩種日志記錄寫入磁盤的時(shí)間點(diǎn)不同,bin log 只在事務(wù)提交完成后進(jìn)行一次寫入,而redo log 在事務(wù)進(jìn)行中就不斷地寫入,表現(xiàn)為日志并不是隨事務(wù)提交的順序進(jìn)行寫入的。
上圖中,bin log 只是在事務(wù)提交時(shí)記錄的,并且對于每一個(gè)事務(wù),只包含對應(yīng)事務(wù)的一個(gè)日志;對于redo log,因?yàn)橛涗浀氖俏锢砣罩?#xff0c;所以每個(gè)事務(wù)對應(yīng)多個(gè)日志條目,并且事務(wù)的redo log 寫入是并發(fā)的,并不是在事務(wù)提交時(shí)寫入,所以在文件中記錄的順序并不是事務(wù)開始時(shí)的順序,*T2 *T3 *T1 表示的是事務(wù)提交時(shí)的日志。
redo log 格式
InnoDB存儲引擎的存儲管理是基于頁的,故重做日志格式也是基于頁的,雖然有著不同的重做日志格式,但是它們有著通用的頭部格式,如下圖所示:
redo_log_type:重做日志的類型。
space:表空間的ID。
page_no:頁的偏移量。
redo log body 部分,根據(jù)重做日志類型的不同嗎,會有不同的存儲內(nèi)容,對于頁上記錄的插入和刪除操作,對應(yīng)如下的格式:
實(shí)現(xiàn)一致性
數(shù)據(jù)庫中的一致性是通過約束來實(shí)現(xiàn)的。如果一個(gè)事務(wù)原子地在一個(gè)一致地?cái)?shù)據(jù)庫中獨(dú)立運(yùn)行,那么在它執(zhí)行之后,數(shù)據(jù)庫的狀態(tài)一定是一致的。對于這個(gè)概念,它的第一層意思就是對于數(shù)據(jù)完整性的約束,包括主鍵約束、引用約束以及一些約束檢查等等,在事務(wù)的執(zhí)行的前后以及過程中不會違背對數(shù)據(jù)完整性的約束,所有對數(shù)據(jù)庫寫入的操作都應(yīng)該是合法的,并不能產(chǎn)生不合法的數(shù)據(jù)狀態(tài)。還是最上面的例子,更新數(shù)據(jù)庫前主鍵都是唯一的,那么更新完改數(shù)據(jù)庫之后主鍵還必須是唯一的。而第二層意思其實(shí)是指邏輯上的對于開發(fā)者的要求,我們要在代碼中寫出正確的事務(wù)邏輯,比如銀行轉(zhuǎn)賬,事務(wù)中的邏輯不可能只扣錢或者只加錢,這是應(yīng)用層面上對于數(shù)據(jù)庫一致性的要求。
事務(wù)的隔離級別
在說事務(wù)的隔離級別之前先來簡單描述一下臟讀、不可重復(fù)讀、幻讀、可重復(fù)讀。
臟讀:臟讀是一個(gè)事務(wù)讀取到了其他事務(wù)還沒有提交的數(shù)據(jù)。
不可重復(fù)讀:指在一個(gè)事務(wù)中,讀到了其他事務(wù)針對就數(shù)據(jù)的修改記錄(常見的操作就是update 或者 delete 語句)。
幻讀:指在一個(gè)事務(wù)中,讀取到了其他事務(wù)新增的數(shù)據(jù),仿佛出現(xiàn)了幻影的現(xiàn)象(常見的操作是insert語句)。
可重復(fù)讀:是mysql默認(rèn)的事務(wù)隔離級別,它消除了臟讀、不可重復(fù)讀、幻讀的現(xiàn)象,保證了事務(wù)的一致性。
SQL標(biāo)準(zhǔn)的事務(wù)隔離級別包括:讀未提交(read uncommitted)、讀提交(read committed)、可重復(fù)讀(repeatable read)和串行化(serializable )。
讀未提交是指,一個(gè)事務(wù)還沒提交時(shí),它做的變更就能被別的事務(wù)看到。
讀提交是指,一個(gè)事務(wù)提交之后,它做的變更才會被其他事務(wù)看到。
可重復(fù)讀是指,一個(gè)事務(wù)執(zhí)行過程中看到的數(shù)據(jù),總是跟這個(gè)事務(wù)在啟動時(shí)看到的數(shù)據(jù)是一致的。當(dāng)然在可重復(fù)讀隔離級別下,未提交變更對其他事務(wù)也是不可見的。
串行化,顧名思義是對于同一行記錄,“寫”會加“寫鎖”,“讀”會加“讀鎖”。當(dāng)出現(xiàn)讀寫鎖沖突的時(shí)候,后訪問的事務(wù)必須等前一個(gè)事務(wù)執(zhí)行完成,才能繼續(xù)執(zhí)行。
下面是按照時(shí)間的順序執(zhí)行兩個(gè)事務(wù)的行為:
mysql> create table T(c int) engine=InnoDB; insert into T(c) values(1);
不同的隔離級別下,事務(wù)A會有哪些不同的返回結(jié)果,也就是圖里面V1、V2、V3的返回值分別是什么:
若隔離級別是“讀未提交”, 則V1的值就是2。這時(shí)候事務(wù)B雖然還沒有提交,但是結(jié)果已經(jīng)被A看到了。因此,V2、V3也都是2。
若隔離級別是“讀提交”,則V1是1,V2的值是2。事務(wù)B的更新在提交后才能被A看到。所以, V3的值也是2。
若隔離級別是“可重復(fù)讀”,則V1、V2是1,V3是2。之所以V2還是1,遵循的就是這個(gè)要求:事務(wù)在執(zhí)行期間看到的數(shù)據(jù)前后必須是一致的。
若隔離級別是“串行化”,則在事務(wù)B執(zhí)行“將1改成2”的時(shí)候,會被鎖住。直到事務(wù)A提交后,事務(wù)B才可以繼續(xù)執(zhí)行。所以從A的角度看, V1、V2值是1,V3的值是2。
隔離級別與臟讀、幻讀等的對應(yīng)關(guān)系:
總結(jié)
以上是生活随笔為你收集整理的Mysql中的事务详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Django框架实现在线考试系统
- 下一篇: html代码圣诞树位置代码,html