詳解MySQL事務(wù)日志undo log
undo log介紹
大家不妨先思考下,如果事務(wù)中的SQL執(zhí)行到一半,遇到報(bào)錯(cuò),需要把前面已經(jīng)執(zhí)行過(guò)的SQL撤銷以達(dá)到原子性的目的,這個(gè)過(guò)程也叫做"回滾",該怎么實(shí)現(xiàn)呢?
每當(dāng)我們要對(duì)一條記錄做改動(dòng)時(shí)(這里的改動(dòng)可以指INSERT、DELETE、UPDATE),把回滾時(shí)所需的東西記下來(lái)。比如:
- 你插入一條記錄時(shí),至少要把這條記錄的主鍵值記下來(lái),之后回滾的時(shí)候只需要把這個(gè)主鍵值對(duì)應(yīng)的記錄刪
掉就好了
- 你刪除了一條記錄,至少要把這條記錄中的內(nèi)容都記下來(lái),這樣之后回滾時(shí)再把由這些內(nèi)容組成的記錄插入
到表中就好了
- 你修改了一條記錄,至少要把修改這條記錄前的舊值都記錄下來(lái),這樣之后回滾時(shí)再把這條記錄更新為舊值
MySQL把這些為了回滾而記錄的這些內(nèi)容稱之為撤銷日志或者回滾日志, 即undo log
。
undo log存儲(chǔ)形式
undo log
的日志內(nèi)容是邏輯日志,非物理日志。
- 物理日志的意思是指具體的把具體某個(gè)數(shù)據(jù)頁(yè)上的某個(gè)偏移量的指改為什么,是具體到物理結(jié)構(gòu)上了,比如
redo log
就是物理日志。 - 而邏輯日志只是記錄了某條數(shù)據(jù)的信息是怎么樣的,沒(méi)有到具體的物理磁盤上
InnoDB
對(duì)undo log
的管理采用段的方式,也就是回滾段(rollback segment
) 。每個(gè)回滾段記錄了 1024 個(gè) undo log segment
,每個(gè)事務(wù)只會(huì)使用一個(gè)回滾段,當(dāng)一個(gè)事務(wù)開(kāi)始的時(shí)候,會(huì)制定一個(gè)回滾段,在事務(wù)進(jìn)行的過(guò)程中,當(dāng)數(shù)據(jù)被修改時(shí),原始的數(shù)據(jù)會(huì)被復(fù)制到回滾段。
在MySQL5.5的時(shí)候,只有一個(gè)回滾段,那么最大同時(shí)支持的事務(wù)數(shù)量為1024個(gè)。在MySQL 5.6開(kāi)始,InnoDB
支持最大 128個(gè)回滾段,故其支持同時(shí)在線的事務(wù)限制提高到了 128*1024 。
- insert undo log格式
記錄的是insert
語(yǔ)句對(duì)應(yīng)的undo log
,它生成的undo log
記錄格式如下圖:
- update undo log格式
記錄的是update、delete
語(yǔ)句對(duì)應(yīng)的undo log
,它生成的undo log
記錄格式如下圖:
那么上面這些生成undo log日志文件最終是存儲(chǔ)在哪的呢?
這些回滾段是存儲(chǔ)于共享表空間ibdata
中。從MySQL5.6版本開(kāi)始,可通過(guò)參數(shù)對(duì)rollback segment
做進(jìn)一步的設(shè)置。這些參數(shù)包括:
innodb_undo_directory
: 設(shè)置rollback segment文件所在的路徑。這意味著rollback segment
可以存放在共享表空間以外的位置,即可以設(shè)置為獨(dú)立表空間。該參數(shù)的默認(rèn)值為“/”,表示當(dāng)前noDB存儲(chǔ)引擎的目錄。innodb_undo_logs
: 設(shè)置rollback segment
的個(gè)數(shù),默認(rèn)值為128。innodb_undo_tablespaces
: 設(shè)置構(gòu)成rollback segment
文件的數(shù)量,這樣rollback segment
可以較為平均地分布在多個(gè)文件中。設(shè)置該參數(shù)后,會(huì)在路徑innodb_undo_directory
看到undo
為前綴的文件,該文件就代表rollback segment
文件。
事務(wù)回滾機(jī)制
對(duì)于InnoDB
引擎來(lái)說(shuō),每個(gè)行記錄除了記錄本身的數(shù)據(jù)之外,還有幾個(gè)隱藏的列:
DB_ROW_ID
:如果沒(méi)有為表顯式的定義主鍵,并且表中也沒(méi)有定義唯一索引,那么InnoDB會(huì)自動(dòng)為表添加一個(gè)row_id的隱藏列作為主鍵。DB_TRX_ID
:每個(gè)事務(wù)都會(huì)分配一個(gè)事務(wù)ID,當(dāng)對(duì)某條記錄發(fā)生變更時(shí),就會(huì)將這個(gè)事務(wù)的事務(wù)ID寫入tx_id
中。
DB_ROLL_PTR
: 回滾指針,本質(zhì)上就是指向undo log
的指針。
- insert數(shù)據(jù):
insert into user (name, sex) values('旭陽(yáng)', '女')
插入的數(shù)據(jù)都會(huì)生成一條insert undo log
,并且數(shù)據(jù)的回滾指針會(huì)指向它。undo log
會(huì)記錄undo log
的序號(hào)、插入主鍵的列和值...,那么在進(jìn)行rollback
的時(shí)候,通過(guò)主鍵直接把對(duì)應(yīng)的數(shù)據(jù)刪除即可。
- update數(shù)據(jù)
update user set sex = '男' where id = 1; update user set name = 'alvin' where id = 1;
這時(shí)會(huì)把老的記錄寫入新的undo log
,讓回滾指針指向新的undo log
,它的undo no
是1,并且新的undo log
會(huì)指向老的undo log(undo no=0)
,最終形成undo log
版本鏈,如下圖所示:
可以發(fā)現(xiàn)每次對(duì)數(shù)據(jù)的變更都會(huì)產(chǎn)生一個(gè)undo log
,當(dāng)一條記錄被變更多次時(shí),那么就會(huì)產(chǎn)生多條undo log
,undo log
記錄的是變更前的日志,并且每個(gè)undo lo
g的序號(hào)是遞增的,那么當(dāng)要回滾的時(shí)候,按照序號(hào)依次向前推,就可以找到我們的原始數(shù)據(jù)了。
那么按照上面的例子,事務(wù)要進(jìn)行回滾,最終得到下面的執(zhí)行流程:
通過(guò)
undo no=2
的日志把id=1的數(shù)據(jù)的name還原成“旭陽(yáng)"通過(guò)
undo no=1
的日志把id=1的數(shù)據(jù)的sex還原成"女"通過(guò)
undo no=0
的日志把id=1的數(shù)據(jù)根據(jù)主鍵信息刪除
undo log生命周期
生成過(guò)程
MySQL處于性能考慮,數(shù)據(jù)會(huì)優(yōu)先從磁盤加載到Buffer Pool
中,在更新Buffer Pool
中數(shù)據(jù)之前,會(huì)優(yōu)先將數(shù)據(jù)記錄到undo log
中。
記錄undo log
日志,MySQL不會(huì)直接去往磁盤中的xx.ibdata
文件寫數(shù)據(jù),而是會(huì)寫在undo_log_buffer
緩沖區(qū)中,因?yàn)楣ぷ骶€程直接去寫磁盤太影響效率了,寫進(jìn)緩沖區(qū)后會(huì)由后臺(tái)線程去刷寫磁盤。
刪除過(guò)程
現(xiàn)在我們已經(jīng)明白了undo log
日志是如何生成,并且作用于事務(wù)回滾的,那這些數(shù)據(jù)是什么時(shí)候刪除呢?
- 針對(duì)于
insert undo log
,因?yàn)?code>insert操作的記錄,只對(duì)事務(wù)本身可見(jiàn),對(duì)其他事務(wù)不可見(jiàn)。故該undo log
在事務(wù)提交后就沒(méi)有用,就會(huì)直接刪除。
● 針對(duì)于update undo log
,該undo log
需要支持MVCC
機(jī)制,因此不能在事務(wù)提交時(shí)就進(jìn)行刪除。提交時(shí)放入undo log
鏈表,有專門的purge
線程進(jìn)行刪除。
總結(jié)
本文詳細(xì)講解了MySQL中undo log
日志的用處,以及它是如何保證事務(wù)的原子性。實(shí)際上,undo log
也還有一個(gè)很重要的作用就是還對(duì)事務(wù)的隔離性實(shí)現(xiàn)起到作用,具體的在后面的MVCC機(jī)制中詳細(xì)說(shuō)明。如果本文對(duì)你有幫助的話,請(qǐng)留下一個(gè)贊吧。
以上就是詳解MySQL事務(wù)日志undo log的詳細(xì)內(nèi)容,更多關(guān)于MySQL undo log的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java格式化輸出詳細(xì)講解(printf、print、println、format等)
Java的格式化輸出等同于String.Format,與C有很大的相似,下面這篇文章主要給大家介紹了關(guān)于Java格式化輸出(printf、print、println、format等)的相關(guān)資料,文中通過(guò)圖文介紹的非常詳細(xì),需要的朋友可以參考下2023-03-03Java Base64算法實(shí)際應(yīng)用之郵件發(fā)送實(shí)例分析
這篇文章主要介紹了Java Base64算法實(shí)際應(yīng)用之郵件發(fā)送,結(jié)合實(shí)例形式分析了java字符編碼與郵件發(fā)送相關(guān)操作技巧,需要的朋友可以參考下2019-09-09smslib發(fā)短信實(shí)例代碼(電腦發(fā)短信)
smslib發(fā)短信實(shí)例,大家可以參考使用開(kāi)發(fā)自己的程序2013-12-12Java?NIO下ByteBuffer的常用方法學(xué)習(xí)
這篇文章主要帶大家來(lái)初步學(xué)習(xí)一下NIO?中的?ByteBuffer的應(yīng)用與常用方法,文中的示例代碼講解詳細(xì),對(duì)我們深入學(xué)習(xí)Java有一定的幫助,感興趣的可以了解一下2023-05-05關(guān)于Java中的try-with-resources語(yǔ)句
這篇文章主要介紹了關(guān)于Java中的try-with-resources語(yǔ)句,try-with-resources是Java中的環(huán)繞語(yǔ)句之一,旨在減輕開(kāi)發(fā)人員釋放try塊中使用的資源的義務(wù),需要的朋友可以參考下2023-05-05如何實(shí)現(xiàn)Spring?Event(異步事件)
這篇文章主要介紹了如何實(shí)現(xiàn)Spring?Event(異步事件)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-02-02Spring Boot啟動(dòng)過(guò)程(六)之內(nèi)嵌Tomcat中StandardHost、StandardContext和Sta
這篇文章主要介紹了Spring Boot啟動(dòng)過(guò)程(六)之內(nèi)嵌Tomcat中StandardHost、StandardContext和StandardWrapper的啟動(dòng)教程詳解,需要的朋友可以參考下2017-04-04SpringBoot結(jié)合Redis實(shí)現(xiàn)接口冪等性的示例代碼
本文主要介紹了SpringBoot結(jié)合Redis實(shí)現(xiàn)接口冪等性的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06