MySQL日志系統(tǒng)中最重要的日志為重做日志redo log和歸檔日志bin log,后者為MySQL Server層的日志,前者為InnoDB存儲引擎層的日志。
1 重做日志redo log
1.1 什么是redo log
redo log用于保證事務(wù)的持久性,即ACID中的D。
持久性:指一個事務(wù)一旦被提交,它對數(shù)據(jù)庫中數(shù)據(jù)的改變就是永久性的,接下來即使數(shù)據(jù)庫發(fā)生故障也不應(yīng)該對其有任何影響。
redo log有兩種類型,分別為物理重做日志和邏輯重做日志。在InnoDB中redo log大多數(shù)情況下是一個物理日志,記錄數(shù)據(jù)頁面的物理變化(實際的數(shù)據(jù)值)。
1.2 redo log的功能
redo log的主要功能是用于數(shù)據(jù)庫崩潰時的數(shù)據(jù)恢復(fù)。
1.3 redo log的組成
redo log可以分為以下兩部分
存儲在內(nèi)存中的重做日志緩沖區(qū)存儲在磁盤上的重做日志文件
![](/d/20211017/a58828f82b5156483ec5b2d8270d3a2d.gif)
1.4 記錄redo log的時機
在完成數(shù)據(jù)的修改之后,臟頁刷入磁盤之前寫入重做日志緩沖區(qū)。即先修改,再寫入。
臟頁:內(nèi)存中與磁盤上不一致的數(shù)據(jù)(并不是壞的!)
在以下情況下,redo log由重做日志緩沖區(qū)寫入磁盤上的重做日志文件。
- redo log buffer的日志占據(jù)redo log buffer總?cè)萘康囊话霑r,將redo log寫入磁盤。
- 一個事務(wù)提交時,他的redo log都刷入磁盤,這樣可以保證數(shù)據(jù)絕不丟失(最常見的情況)。注意這時內(nèi)存中的臟頁可能尚未全部寫入磁盤。
- 后臺線程定時刷新,有一個后臺線程每過一秒就將redo log寫入磁盤。
- MySQL關(guān)閉時,redo log都被寫入磁盤。
第一種情況和第四種情況一定會執(zhí)行redo log的寫入,第二種情況和第三種情況的執(zhí)行要根據(jù)參數(shù)innodb_flush_log_at_trx_commit
的設(shè)定值,在下文會有詳細(xì)描述。
索引的創(chuàng)建也需要記錄redo log。
1.5 一個重做全過程的示例
![](/d/20211017/0290840c220c0ce2ca6d3e7dff5c17d6.gif)
以更新事務(wù)為例。
- 將原始數(shù)據(jù)讀入內(nèi)存,修改數(shù)據(jù)的內(nèi)存副本。
- 生成redo log并寫入重做日志緩沖區(qū),redo log中存儲的是修改后的新值。
- 事務(wù)提交時,將重做日志緩沖區(qū)中的內(nèi)容刷新到重做日志文件。
- 隨后正常將內(nèi)存中的臟頁刷回磁盤。
1.6 持久性的保證
1.6.1 Force Log at Commit機制
Force Log at Commit機制實現(xiàn)了事務(wù)的持久性。在內(nèi)存中操作時,日志被寫入重做日志緩沖區(qū)。但在事務(wù)提交之前,必須首先將所有日志寫入磁盤上的重做日志文件。
為了確保每個日志都寫入重做日志文件,必須使用一個fsync系統(tǒng)調(diào)用,確保OS buffer中的日志被完整地寫入磁盤上的log file。
fsync系統(tǒng)調(diào)用:需要你在入?yún)⒌奈恢蒙蟼鬟f給他一個fd,然后系統(tǒng)調(diào)用就會對這個fd指向的文件起作用。fsync會確保一直到寫磁盤操作結(jié)束才會返回,所以當(dāng)你的程序使用這個函數(shù)并且它成功返回時,就說明數(shù)據(jù)肯定已經(jīng)安全的落盤了。所以fsync適合數(shù)據(jù)庫這種程序。
![](/d/20211017/2e2a9ae8191b9494f7457f32cc30d145.gif)
1.6.2 innodb_flush_log_at_trx_commit參數(shù)
InnoDB提供了一個參數(shù)innodb_flush_log_at_trx_commit
控制日志刷新到磁盤的策略。
- 當(dāng)
innodb_flush_log_at_trx_commit
值為1時(默認(rèn))。事務(wù)每次提交都必須將log buffer中的日志寫入os buffer并調(diào)用fsync()寫入磁盤中。
這種方式即使系統(tǒng)崩潰也不會丟失任何數(shù)據(jù),但是因為每次提交都寫入磁盤,IO性能較差。
- 當(dāng)
innodb_flush_log_at_trx_commit
值為0時。事務(wù)提交時不將log buffer寫入到os buffer,而是每秒寫入os buffer并調(diào)用fsync()寫入到log file on disk中。
這實際上相當(dāng)于在內(nèi)存中維護了一個用戶設(shè)計的緩沖區(qū),它減少了和os buffer之間的數(shù)據(jù)傳輸,有更好的性能。
每秒寫入磁盤,系統(tǒng)崩潰會丟失1s的數(shù)據(jù)。
- 當(dāng)
innodb_flush_log_at_trx_commit
值為2時。每次提交都僅寫入os buffer,然后每秒調(diào)用fsync()將os buffer中的日志寫入到log file on disk中。
雖然說我們是每秒調(diào)用fsync()將os buffer中的日志寫入到log file on disk中,但是平時即使不調(diào)用fsync,數(shù)據(jù)也會2自主地逐漸進入磁盤。所以當(dāng)發(fā)生系統(tǒng)崩潰,相比第二種情況,會丟失較少的數(shù)據(jù)。
但同時,由于每次提交都寫入os buffer,所以相比第二種情況,性能會差一些,但還是比第一種好的。
無論是哪種情況
![](/d/20211017/85c053c1270b0d591030c1edf8c6179e.gif)
1.6.3 一個小的性能測試
幾個選項之間的性能差距是極大的,下面做一個簡單的測試。
#創(chuàng)建測試表
drop table if exists test_flush_log;
create table test_flush_log(id int,name char(50))engine=innodb;
#創(chuàng)建插入指定行數(shù)的記錄到測試表中的存儲過程
drop procedure if exists proc;
delimiter $$
create procedure proc(i int)
begin
declare s int default 1;
declare c char(50) default repeat('a',50);
while s=i do
start transaction;
insert into test_flush_log values(null,c);
commit;
set s=s+1;
end while;
end$$
delimiter ;
下面均插入十萬條記錄。
Ⅰ 當(dāng)innodb_flush_log_at_trx_commit
值為1時
test> call proc(100000)
[2021-07-25 13:22:02] completed in 27 s 350 ms
需要長達27.35s。
Ⅱ 當(dāng)innodb_flush_log_at_trx_commit
值為2時
test> set @@global.innodb_flush_log_at_trx_commit=2;
test> truncate test_flush_log;
test> call proc(100000)
[2021-07-25 13:27:33] completed in 5 s 774 ms
只需5.774s,性能大大提升。
Ⅲ 當(dāng)innodb_flush_log_at_trx_commit
值為0時
test> set @@global.innodb_flush_log_at_trx_commit=0;
test> truncate test_flush_log;
test> call proc(100000)
[2021-07-25 13:30:34] completed in 3 s 537 ms
只需3.537s,性能更高。
顯然,innodb_flush_log_at_trx_commit
值為1時性能差得非常明顯,改為0和2后性能都有大幅提升,其中0更快但相比2提升不大。
雖然改為0和2可以大幅提升性能,但會嚴(yán)重影響安全性。我們可以通過修改存儲過程,將事務(wù)的創(chuàng)建和提交放到循環(huán)外,統(tǒng)一提交,減少了IO頻率。
drop procedure if exists proc;
delimiter $$
create procedure proc(i int)
begin
declare s int default 1;
declare c char(50) default repeat('a',50);
start transaction;
while s=i DO
insert into test_flush_log values(null,c);
set s=s+1;
end while;
commit;
end$$
delimiter ;
1.6.4 迷你事務(wù)mini-transaction
mini-trasaction是InnoDB處理小型事務(wù)時使用的一種機制,它可以確保并發(fā)事務(wù)操作和數(shù)據(jù)庫異常發(fā)生時,數(shù)據(jù)頁中的數(shù)據(jù)一致性。
迷你事務(wù)必須遵循下面三個協(xié)議:
- FIX規(guī)則。寫時必須使用獨占鎖,讀時必須使用共享鎖。反正就是要鎖住。
- 預(yù)寫日志。預(yù)寫日志即WAL,Write-Ahead Log。持久化數(shù)據(jù)之前,必須先持久化內(nèi)存中的日志。每個頁面都有一個LSN(日志序列號)。在將數(shù)據(jù)寫入磁盤前,要先將內(nèi)存中序列號小于LSN的日志寫入磁盤。WAL提供三種持久化模式
最嚴(yán)格的是full-sync,fsync保證在返回之前將記錄刷新到磁盤,最大化了數(shù)據(jù)的安全性。
![](/d/20211017/2ede684a0acec6a20b076d6aaea1112d.gif)
第二個級別是write-only,保證記錄寫入操作系統(tǒng)。這允許數(shù)據(jù)在進程級別的崩潰后幸存。
![](/d/20211017/33765e12fbd0b35a5fd78761568a4a09.gif)
最不嚴(yán)格的是no-sync,將記錄保存在內(nèi)存緩沖區(qū)中,不保證立即寫入文件系統(tǒng)。
![](/d/20211017/2477b2995a0d8466088ccc91d8154604.gif)
強制日志再提交。即Force-log-at-commit,它要求提交事務(wù)時必須把所有迷你事務(wù)日志刷新到磁盤。
1.7 寫redo log的過程
![](/d/20211017/aaf0d0a72868d1c4d33fc2fcc945cf15.gif)
如上圖,展示了redo log是如何被寫入log buffer的。每個mini-trasaction對應(yīng)于每個DML操作,例如更新語句等。
- 每個數(shù)據(jù)修改后被寫入迷你事務(wù)私有緩沖區(qū)。
- 當(dāng)更新語句完成,redo log從迷你事務(wù)私有緩沖區(qū)被寫入內(nèi)存中的公共日志緩沖區(qū)。
- 提交外部事務(wù)時,會將重做日志緩沖區(qū)刷入重做日志文件。
1.8 日志塊 log block
redo log以塊為單位進行存儲,每個塊大小為512字節(jié)。無論是在內(nèi)存重做日志緩沖區(qū)、操作系統(tǒng)緩沖區(qū)還是重做日志文件中,都是以這樣的512字節(jié)大小的塊進行存儲的。
![](/d/20211017/2eb6989f835c5b2225c96d90ebae8ecf.gif)
每個日志塊頭由以下四個部分組成
- log_block_hdr_no:(4字節(jié))該日志塊在redo log buffer中的位置ID。
- log_block_hdr_data_len:(2字節(jié))該log block中已記錄的log大小。寫滿該log block時為0x200,表示512字節(jié)。
- log_block_first_rec_group:(2字節(jié))該log block中第一個log的開始偏移位置。
- lock_block_checkpoint_no:(4字節(jié))寫入檢查點信息的位置。
1.9 log group
log group代表redo log的分組,由多個大小相同的redo log file組成。由一個參數(shù)innodb_log_files_group
決定,默認(rèn)為2。
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜img-qAyaSeL3543740G:61311akw89MySQL[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-h01w68EG-1627284031849)(G:\markdown\MySQL\image-20210726131134489.png)].png)]
這個group是邏輯上的概念,但可以通過變量 innodb_log_group_home_dir
來定義組的目錄,redo log file都放在這個目錄下,默認(rèn)是在datadir下。
![](/d/20211017/da31ce98d314eefdb573ce314be843d3.gif)
2 撤銷日志undo log
2.1 關(guān)于undo log
undo log存在的意義是確保數(shù)據(jù)庫事務(wù)的原子性。
原子性是指事務(wù)是一個不可分割的工作單位,事務(wù)中的操作要么都發(fā)生,要么都不發(fā)生。
- edo log記錄了事務(wù)的行為,可以很好地保證一致性,對數(shù)據(jù)進行“重做”操作。但事務(wù)有時還需要進行“回滾”操作,這時就需要undo log。當(dāng)我們對記錄做了變更操作的時候就需要產(chǎn)生undo log,其中記錄的是老版本的數(shù)據(jù),當(dāng)舊事務(wù)需要讀取數(shù)據(jù)時,可以順著undo鏈找到滿足其可見性地記錄。
- undo log通常以邏輯日志的形式存在。我們可以認(rèn)為當(dāng)delete一條記錄時,undo log會產(chǎn)生一條對應(yīng)的insert記錄,反之亦然。當(dāng)update一條記錄時,會產(chǎn)生一條相反的update記錄。
- undo log采用段segment的方式來記錄,每個undo操作在記錄的時候占用一個undo log segment。
- undo log也會產(chǎn)生redo log,因為undo log也要實現(xiàn)持久性保護。
undo log通常以邏輯日志的形式存在。我們可以認(rèn)為當(dāng)delete一條記錄時,undo log會產(chǎn)生一條對應(yīng)的insert記錄,反之亦然。當(dāng)update一條記錄時,會產(chǎn)生一條相反的update記錄。
undo log采用段segment的方式來記錄,每個undo操作在記錄的時候占用一個undo log segment。
undo log也會產(chǎn)生redo log,因為undo log也要實現(xiàn)持久性保護。
2.2 undo log segment
為了保證事務(wù)并發(fā)操作時,寫各自的undo log時不發(fā)生沖突,nnodb用段的方式管理undo log。rollback segment稱為回滾段,每個回滾段中有1024個undo log segment。MySQL5.5以后的版本支持128個rollback segment,就可以存儲128*1024個操作,還可以通過innodb_undo_logs
參數(shù)定義盯梢個rollback segment。
![](/d/20211017/f39329977c90d87ac46a6420cf2f2b1c.gif)
2.3 purge
在聚集索引列的操作中,MySQL是這樣設(shè)計的。對一條delete語句
delete from t where a = 1
假如a有聚集索引(主鍵),那么不會進行真正的刪除,而是在主鍵列等于1的記錄處設(shè)置delete flag為1,即把記錄保存在B+樹中。同理,對于update操作,不是直接更新記錄,而是把舊紀(jì)錄標(biāo)識為刪除,再創(chuàng)建一條新記錄。
那么,舊版本記錄什么時候真正的刪除呢?
InnoDB使用undo日志進行舊版本的刪除操作,這個操作稱為purge操作。InnoDB開辟了purge線程進行purge操作,并且可以控制purge線程的數(shù)量,每個purge線程每10s 進行一次purge操作。
InnoDB的undo log設(shè)計
一個頁上允許多個事務(wù)的undo log存在,undo log的存儲順序是隨時的。InnoDB維護了一個history鏈表,按照事務(wù)提交的順序?qū)ndo log進行連接。
![](/d/20211017/ed2a688b0baa8769696baa92918b364d.gif)
在執(zhí)行purge過程中,InnoDB存儲引擎首先從history list中找到第一個需要被清理的記錄,這里為trx1,清理之后InnoDB存儲引擎會在trx1所在的Undo page中繼續(xù)尋找是否存在可以被清理的記錄,這里會找到事務(wù)trx3,接著找到trx5,但是發(fā)現(xiàn)trx5被其他事務(wù)所引用而不能清理,故再去history list中取查找,發(fā)現(xiàn)最尾端的記錄時trx2,接著找到trx2所在的Undo page,依次把trx6、trx4清理,由于Undo page2中所有的記錄都被清理了,因此該Undo page可以進行重用。
InnoDB存儲引擎這種先從history list中找undo log,然后再從Undo page中找undo log的設(shè)計模式是為了避免大量隨機讀操作,從而提高purge的效率。
3 InnoDB的恢復(fù)操作
3.1 數(shù)據(jù)頁刷盤的規(guī)則和checkpoint
內(nèi)存中(buffer pool)未刷到磁盤的數(shù)據(jù)稱為臟數(shù)據(jù)(dirty data)。由于數(shù)據(jù)和日志都以頁的形式存在,所以臟頁表示臟數(shù)據(jù)和臟日志。
在InnoDB中,checkpoint是數(shù)據(jù)刷盤的唯一規(guī)則。checkpoint觸發(fā)后,會將內(nèi)存中的臟數(shù)據(jù)刷到磁盤。
innodb存儲引擎中checkpoint分為兩種:
- sharp checkpoint:在重用redo log文件(例如切換日志文件)的時候,將所有已記錄到redo log中對應(yīng)的臟數(shù)據(jù)刷到磁盤。
- fuzzy checkpoint:一次只刷一小部分的日志到磁盤,而非將所有臟日志刷盤。有以下幾種情況會觸發(fā)該檢查點:
master thread checkpoint。由master線程控制,每秒或每10秒刷入一定比例的臟頁到磁盤。
flush_lru_list checkpoint。從MySQL5.6開始可通過 innodb_page_cleaners 變量指定專門負(fù)責(zé)臟頁刷盤的page cleaner線程的個數(shù),該線程的目的是為了保證lru列表有可用的空閑頁。
async/sync flush checkpoint。同步刷盤還是異步刷盤。例如還有非常多的臟頁沒刷到磁盤(非常多是多少,有比例控制),這時候會選擇同步刷到磁盤,但這很少出現(xiàn);如果臟頁不是很多,可以選擇異步刷到磁盤,如果臟頁很少,可以暫時不刷臟頁到磁盤
dirty page too much checkpoint。臟頁太多時強制觸發(fā)檢查點,目的是為了保證緩存有足夠的空閑空間。too much的比例由變量 innodb_max_dirty_pages_pct 控制,MySQL 5.6默認(rèn)的值為75,即當(dāng)臟頁占緩沖池的百分之75后,就強制刷一部分臟頁到磁盤。
由于刷臟頁需要一定的時間來完成,所以記錄檢查點的位置是在每次刷盤結(jié)束之后才在redo log中標(biāo)記的。
3.2 LSN
3.2.1 LSN概念
LSN稱為日志的邏輯序列號,在InnoDB中占用8個字節(jié)
我們可以通過LSN了解到下面這些信息:
- 數(shù)據(jù)頁的版本信息。
- 寫入的日志總量。
- 檢查點的位置。
在下面兩個位置存在LSN:
- redo log的記錄中。
- 每個數(shù)據(jù)頁的頭部有一個變量
fil_page_lsn
記錄了本頁最終的LSN值是多少。
顯然,如果頁中的LSN值小于redo log中的LSN值,說明數(shù)據(jù)出現(xiàn)了丟失。
通過show engine innodb status
可以查看當(dāng)前InnoDB的運行信息,其中有一欄log中有關(guān)于lsn的記錄。
![](/d/20211017/ab23c719df9b977f17dd6e73c117bc68.gif)
- log sequence number記錄了當(dāng)前的redo log(in buffer)中的LSN。
- log flushed up to是刷到磁盤重做日志文件中的LSN。
- pages flushed up to是已經(jīng)刷到磁盤數(shù)據(jù)頁上的LSN。
- last checkpoint at是上一次檢查點所在位置的LSN。
3.2.2 LSN處理流程
(1).首先修改內(nèi)存中的數(shù)據(jù)頁,并在數(shù)據(jù)頁中記錄LSN,暫且稱之為data_in_buffer_lsn;
(2).并且在修改數(shù)據(jù)頁的同時(幾乎是同時)向redo log in buffer中寫入redo log,并記錄下對應(yīng)的LSN,暫且稱之為redo_log_in_buffer_lsn;
(3).寫完buffer中的日志后,當(dāng)觸發(fā)了日志刷盤的幾種規(guī)則時,會向redo log file on disk刷入重做日志,并在該文件中記下對應(yīng)的LSN,暫且稱之為redo_log_on_disk_lsn;
(4).數(shù)據(jù)頁不可能永遠(yuǎn)只停留在內(nèi)存中,在某些情況下,會觸發(fā)checkpoint來將內(nèi)存中的臟頁(數(shù)據(jù)臟頁和日志臟頁)刷到磁盤,所以會在本次checkpoint臟頁刷盤結(jié)束時,在redo log中記錄checkpoint的LSN位置,暫且稱之為checkpoint_lsn。
(5).要記錄checkpoint所在位置很快,只需簡單的設(shè)置一個標(biāo)志即可,但是刷數(shù)據(jù)頁并不一定很快,例如這一次checkpoint要刷入的數(shù)據(jù)頁非常多。也就是說要刷入所有的數(shù)據(jù)頁需要一定的時間來完成,中途刷入的每個數(shù)據(jù)頁都會記下當(dāng)前頁所在的LSN,暫且稱之為data_page_on_disk_lsn。
![](/d/20211017/20f2d2b4f7b1e941562b3d0df9b7094d.gif)
上圖中,從上到下的橫線分別代表:時間軸、buffer中數(shù)據(jù)頁中記錄的LSN(data_in_buffer_lsn)、磁盤中數(shù)據(jù)頁中記錄的LSN(data_page_on_disk_lsn)、buffer中重做日志記錄的LSN(redo_log_in_buffer_lsn)、磁盤中重做日志文件中記錄的LSN(redo_log_on_disk_lsn)以及檢查點記錄的LSN(checkpoint_lsn)。
假設(shè)在最初時(12:0:00)所有的日志頁和數(shù)據(jù)頁都完成了刷盤,也記錄好了檢查點的LSN,這時它們的LSN都是完全一致的。
假設(shè)此時開啟了一個事務(wù),并立刻執(zhí)行了一個update操作,執(zhí)行完成后,buffer中的數(shù)據(jù)頁和redo log都記錄好了更新后的LSN值,假設(shè)為110。這時候如果執(zhí)行 show engine innodb status 查看各LSN的值,即圖中①處的位置狀態(tài),結(jié)果會是:
log sequence number(110) > log flushed up to(100) = pages flushed up to = last checkpoint at
之后又執(zhí)行了一個delete語句,LSN增長到150。等到12:00:01時,觸發(fā)redo log刷盤的規(guī)則(其中有一個規(guī)則是 innodb_flush_log_at_timeout 控制的默認(rèn)日志刷盤頻率為1秒),這時redo log file on disk中的LSN會更新到和redo log in buffer的LSN一樣,所以都等于150,這時 show engine innodb status ,即圖中②的位置,結(jié)果將會是:
log sequence number(150) = log flushed up to > pages flushed up to(100) = last checkpoint at
再之后,執(zhí)行了一個update語句,緩存中的LSN將增長到300,即圖中③的位置。
假設(shè)隨后檢查點出現(xiàn),即圖中④的位置,正如前面所說,檢查點會觸發(fā)數(shù)據(jù)頁和日志頁刷盤,但需要一定的時間來完成,所以在數(shù)據(jù)頁刷盤還未完成時,檢查點的LSN還是上一次檢查點的LSN,但此時磁盤上數(shù)據(jù)頁和日志頁的LSN已經(jīng)增長了,即:
log sequence number > log flushed up to 和 pages flushed up to > last checkpoint at
但是log flushed up to和pages flushed up to的大小無法確定,因為日志刷盤可能快于數(shù)據(jù)刷盤,也可能等于,還可能是慢于。但是checkpoint機制有保護數(shù)據(jù)刷盤速度是慢于日志刷盤的:當(dāng)數(shù)據(jù)刷盤速度超過日志刷盤時,將會暫時停止數(shù)據(jù)刷盤,等待日志刷盤進度超過數(shù)據(jù)刷盤。
等到數(shù)據(jù)頁和日志頁刷盤完畢,即到了位置⑤的時候,所有的LSN都等于300。
隨著時間的推移到了12:00:02,即圖中位置⑥,又觸發(fā)了日志刷盤的規(guī)則,但此時buffer中的日志LSN和磁盤中的日志LSN是一致的,所以不執(zhí)行日志刷盤,即此時 show engine innodb status 時各種lsn都相等。
隨后執(zhí)行了一個insert語句,假設(shè)buffer中的LSN增長到了800,即圖中位置⑦。此時各種LSN的大小和位置①時一樣。
隨后執(zhí)行了提交動作,即位置⑧。默認(rèn)情況下,提交動作會觸發(fā)日志刷盤,但不會觸發(fā)數(shù)據(jù)刷盤,所以 show engine innodb status 的結(jié)果是:
log sequence number = log flushed up to > pages flushed up to = last checkpoint at
最后隨著時間的推移,檢查點再次出現(xiàn),即圖中位置⑨。但是這次檢查點不會觸發(fā)日志刷盤,因為日志的LSN在檢查點出現(xiàn)之前已經(jīng)同步了。假設(shè)這次數(shù)據(jù)刷盤速度極快,快到一瞬間內(nèi)完成而無法捕捉到狀態(tài)的變化,這時 show engine innodb status 的結(jié)果將是各種LSN相等。
3.3 InnoDB的恢復(fù)行為
啟動InnoDB時,一定會進行恢復(fù)操作,無論上次是因為什么原因退出。
checkpoint表示已經(jīng)完整刷到磁盤上data page上的LSN,因此恢復(fù)時僅需要恢復(fù)從checkpoint開始的日志部分。例如,當(dāng)數(shù)據(jù)庫在上一次checkpoint的LSN為10000時宕機,且事務(wù)是已經(jīng)提交過的狀態(tài)。啟動數(shù)據(jù)庫時會檢查磁盤中數(shù)據(jù)頁的LSN,如果數(shù)據(jù)頁的LSN小于日志中的LSN,則會從檢查點開始恢復(fù)。
還有一種情況,在宕機前正處于checkpoint的刷盤過程,且數(shù)據(jù)頁的刷盤進度超過了日志頁的刷盤進度。這時候一宕機,數(shù)據(jù)頁中記錄的LSN就會大于日志頁中的LSN,在重啟的恢復(fù)過程中會檢查到這一情況,這時超出日志進度的部分將不會重做,因為這本身就表示已經(jīng)做過的事情,無需再重做。
另外,事務(wù)日志具有冪等性,所以多次操作得到同一結(jié)果的行為在日志中只記錄一次。而二進制日志不具有冪等性,多次操作會全部記錄下來,在恢復(fù)的時候會多次執(zhí)行二進制日志中的記錄,速度就慢得多。例如,某記錄中id初始值為2,通過update將值設(shè)置為了3,后來又設(shè)置成了2,在事務(wù)日志中記錄的將是無變化的頁,根本無需恢復(fù);而二進制會記錄下兩次update操作,恢復(fù)時也將執(zhí)行這兩次update操作,速度比事務(wù)日志恢復(fù)更慢。
到此這篇關(guān)于MySQL中的redo log和undo log的文章就介紹到這了,更多相關(guān)MySQL中的redo log和undo log內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
您可能感興趣的文章:- MySQL系列之redo log、undo log和binlog詳解
- 詳解MySQL 重做日志(redo log)與回滾日志(undo logo)
- MySQL 撤銷日志與重做日志(Undo Log與Redo Log)相關(guān)總結(jié)
- 基于Redo Log和Undo Log的MySQL崩潰恢復(fù)解析
- Mysql中undo、redo與binlog的區(qū)別淺析