佳木斯湛栽影视文化发展公司

主頁 > 知識庫 > MySQL中的redo log和undo log日志詳解

MySQL中的redo log和undo log日志詳解

熱門標(biāo)簽:呼叫中心市場需求 鐵路電話系統(tǒng) Linux服務(wù)器 地方門戶網(wǎng)站 網(wǎng)站排名優(yōu)化 服務(wù)外包 AI電銷 百度競價(jià)排名

MySQL日志系統(tǒng)中最重要的日志為重做日志redo log和歸檔日志bin log,后者為MySQL Server層的日志,前者為InnoDB存儲(chǔ)引擎層的日志。

1 重做日志redo log

1.1 什么是redo log

redo log用于保證事務(wù)的持久性,即ACID中的D。

持久性:指一個(gè)事務(wù)一旦被提交,它對數(shù)據(jù)庫中數(shù)據(jù)的改變就是永久性的,接下來即使數(shù)據(jù)庫發(fā)生故障也不應(yīng)該對其有任何影響。

redo log有兩種類型,分別為物理重做日志和邏輯重做日志。在InnoDB中redo log大多數(shù)情況下是一個(gè)物理日志,記錄數(shù)據(jù)頁面的物理變化(實(shí)際的數(shù)據(jù)值)。

1.2 redo log的功能

redo log的主要功能是用于數(shù)據(jù)庫崩潰時(shí)的數(shù)據(jù)恢復(fù)。

1.3 redo log的組成

redo log可以分為以下兩部分

存儲(chǔ)在內(nèi)存中的重做日志緩沖區(qū)存儲(chǔ)在磁盤上的重做日志文件

1.4 記錄redo log的時(shí)機(jī)

在完成數(shù)據(jù)的修改之后,臟頁刷入磁盤之前寫入重做日志緩沖區(qū)。即先修改,再寫入。

臟頁:內(nèi)存中與磁盤上不一致的數(shù)據(jù)(并不是壞的!)

在以下情況下,redo log由重做日志緩沖區(qū)寫入磁盤上的重做日志文件。

  • redo log buffer的日志占據(jù)redo log buffer總?cè)萘康囊话霑r(shí),將redo log寫入磁盤。
  • 一個(gè)事務(wù)提交時(shí),他的redo log都刷入磁盤,這樣可以保證數(shù)據(jù)絕不丟失(最常見的情況)。注意這時(shí)內(nèi)存中的臟頁可能尚未全部寫入磁盤。
  • 后臺(tái)線程定時(shí)刷新,有一個(gè)后臺(tái)線程每過一秒就將redo log寫入磁盤。
  • MySQL關(guān)閉時(shí),redo log都被寫入磁盤。

第一種情況和第四種情況一定會(huì)執(zhí)行redo log的寫入,第二種情況和第三種情況的執(zhí)行要根據(jù)參數(shù)innodb_flush_log_at_trx_commit的設(shè)定值,在下文會(huì)有詳細(xì)描述。

索引的創(chuàng)建也需要記錄redo log。

1.5 一個(gè)重做全過程的示例

以更新事務(wù)為例。

  • 將原始數(shù)據(jù)讀入內(nèi)存,修改數(shù)據(jù)的內(nèi)存副本。
  • 生成redo log并寫入重做日志緩沖區(qū),redo log中存儲(chǔ)的是修改后的新值。
  • 事務(wù)提交時(shí),將重做日志緩沖區(qū)中的內(nèi)容刷新到重做日志文件。
  • 隨后正常將內(nèi)存中的臟頁刷回磁盤。

1.6 持久性的保證

1.6.1 Force Log at Commit機(jī)制

Force Log at Commit機(jī)制實(shí)現(xiàn)了事務(wù)的持久性。在內(nèi)存中操作時(shí),日志被寫入重做日志緩沖區(qū)。但在事務(wù)提交之前,必須首先將所有日志寫入磁盤上的重做日志文件。

為了確保每個(gè)日志都寫入重做日志文件,必須使用一個(gè)fsync系統(tǒng)調(diào)用,確保OS buffer中的日志被完整地寫入磁盤上的log file。

fsync系統(tǒng)調(diào)用:需要你在入?yún)⒌奈恢蒙蟼鬟f給他一個(gè)fd,然后系統(tǒng)調(diào)用就會(huì)對這個(gè)fd指向的文件起作用。fsync會(huì)確保一直到寫磁盤操作結(jié)束才會(huì)返回,所以當(dāng)你的程序使用這個(gè)函數(shù)并且它成功返回時(shí),就說明數(shù)據(jù)肯定已經(jīng)安全的落盤了。所以fsync適合數(shù)據(jù)庫這種程序。

1.6.2 innodb_flush_log_at_trx_commit參數(shù)

InnoDB提供了一個(gè)參數(shù)innodb_flush_log_at_trx_commit控制日志刷新到磁盤的策略。

  • 當(dāng)innodb_flush_log_at_trx_commit值為1時(shí)(默認(rèn))。事務(wù)每次提交都必須將log buffer中的日志寫入os buffer并調(diào)用fsync()寫入磁盤中。

這種方式即使系統(tǒng)崩潰也不會(huì)丟失任何數(shù)據(jù),但是因?yàn)槊看翁峤欢紝懭氪疟P,IO性能較差。

  • 當(dāng)innodb_flush_log_at_trx_commit值為0時(shí)。事務(wù)提交時(shí)不將log buffer寫入到os buffer,而是每秒寫入os buffer并調(diào)用fsync()寫入到log file on disk中。

這實(shí)際上相當(dāng)于在內(nèi)存中維護(hù)了一個(gè)用戶設(shè)計(jì)的緩沖區(qū),它減少了和os buffer之間的數(shù)據(jù)傳輸,有更好的性能。

每秒寫入磁盤,系統(tǒng)崩潰會(huì)丟失1s的數(shù)據(jù)。

  • 當(dāng)innodb_flush_log_at_trx_commit值為2時(shí)。每次提交都僅寫入os buffer,然后每秒調(diào)用fsync()將os buffer中的日志寫入到log file on disk中。

雖然說我們是每秒調(diào)用fsync()將os buffer中的日志寫入到log file on disk中,但是平時(shí)即使不調(diào)用fsync,數(shù)據(jù)也會(huì)2自主地逐漸進(jìn)入磁盤。所以當(dāng)發(fā)生系統(tǒng)崩潰,相比第二種情況,會(huì)丟失較少的數(shù)據(jù)。

但同時(shí),由于每次提交都寫入os buffer,所以相比第二種情況,性能會(huì)差一些,但還是比第一種好的。

無論是哪種情況

1.6.3 一個(gè)小的性能測試

幾個(gè)選項(xiàng)之間的性能差距是極大的,下面做一個(gè)簡單的測試。

#創(chuàng)建測試表
drop table if exists test_flush_log;
create table test_flush_log(id int,name char(50))engine=innodb;

#創(chuàng)建插入指定行數(shù)的記錄到測試表中的存儲(chǔ)過程
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時(shí)

test> call proc(100000)
[2021-07-25 13:22:02] completed in 27 s 350 ms

需要長達(dá)27.35s。

Ⅱ 當(dāng)innodb_flush_log_at_trx_commit值為2時(shí)

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時(shí)

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時(shí)性能差得非常明顯,改為0和2后性能都有大幅提升,其中0更快但相比2提升不大。

雖然改為0和2可以大幅提升性能,但會(huì)嚴(yán)重影響安全性。我們可以通過修改存儲(chǔ)過程,將事務(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ù)時(shí)使用的一種機(jī)制,它可以確保并發(fā)事務(wù)操作和數(shù)據(jù)庫異常發(fā)生時(shí),數(shù)據(jù)頁中的數(shù)據(jù)一致性。

迷你事務(wù)必須遵循下面三個(gè)協(xié)議:

  • FIX規(guī)則。寫時(shí)必須使用獨(dú)占鎖,讀時(shí)必須使用共享鎖。反正就是要鎖住。
  • 預(yù)寫日志。預(yù)寫日志即WAL,Write-Ahead Log。持久化數(shù)據(jù)之前,必須先持久化內(nèi)存中的日志。每個(gè)頁面都有一個(gè)LSN(日志序列號)。在將數(shù)據(jù)寫入磁盤前,要先將內(nèi)存中序列號小于LSN的日志寫入磁盤。WAL提供三種持久化模式

最嚴(yán)格的是full-sync,fsync保證在返回之前將記錄刷新到磁盤,最大化了數(shù)據(jù)的安全性。

第二個(gè)級別是write-only,保證記錄寫入操作系統(tǒng)。這允許數(shù)據(jù)在進(jìn)程級別的崩潰后幸存。

最不嚴(yán)格的是no-sync,將記錄保存在內(nèi)存緩沖區(qū)中,不保證立即寫入文件系統(tǒng)。

強(qiáng)制日志再提交。即Force-log-at-commit,它要求提交事務(wù)時(shí)必須把所有迷你事務(wù)日志刷新到磁盤。

1.7 寫redo log的過程

如上圖,展示了redo log是如何被寫入log buffer的。每個(gè)mini-trasaction對應(yīng)于每個(gè)DML操作,例如更新語句等。

  • 每個(gè)數(shù)據(jù)修改后被寫入迷你事務(wù)私有緩沖區(qū)。
  • 當(dāng)更新語句完成,redo log從迷你事務(wù)私有緩沖區(qū)被寫入內(nèi)存中的公共日志緩沖區(qū)。
  • 提交外部事務(wù)時(shí),會(huì)將重做日志緩沖區(qū)刷入重做日志文件。

1.8 日志塊 log block

redo log以塊為單位進(jìn)行存儲(chǔ),每個(gè)塊大小為512字節(jié)。無論是在內(nèi)存重做日志緩沖區(qū)、操作系統(tǒng)緩沖區(qū)還是重做日志文件中,都是以這樣的512字節(jié)大小的塊進(jìn)行存儲(chǔ)的。

每個(gè)日志塊頭由以下四個(gè)部分組成

  • log_block_hdr_no:(4字節(jié))該日志塊在redo log buffer中的位置ID。
  • log_block_hdr_data_len:(2字節(jié))該log block中已記錄的log大小。寫滿該log block時(shí)為0x200,表示512字節(jié)。
  • log_block_first_rec_group:(2字節(jié))該log block中第一個(gè)log的開始偏移位置。
  • lock_block_checkpoint_no:(4字節(jié))寫入檢查點(diǎn)信息的位置。

1.9 log group

log group代表redo log的分組,由多個(gè)大小相同的redo log file組成。由一個(gè)參數(shù)innodb_log_files_group決定,默認(rèn)為2。
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜img-qAyaSeL3543740G:61311akw89MySQL[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-h01w68EG-1627284031849)(G:\markdown\MySQL\image-20210726131134489.png)].png)]

這個(gè)group是邏輯上的概念,但可以通過變量 innodb_log_group_home_dir 來定義組的目錄,redo log file都放在這個(gè)目錄下,默認(rèn)是在datadir下。

2 撤銷日志undo log

2.1 關(guān)于undo log

undo log存在的意義是確保數(shù)據(jù)庫事務(wù)的原子性。

原子性是指事務(wù)是一個(gè)不可分割的工作單位,事務(wù)中的操作要么都發(fā)生,要么都不發(fā)生。

  • edo log記錄了事務(wù)的行為,可以很好地保證一致性,對數(shù)據(jù)進(jìn)行“重做”操作。但事務(wù)有時(shí)還需要進(jìn)行“回滾”操作,這時(shí)就需要undo log。當(dāng)我們對記錄做了變更操作的時(shí)候就需要產(chǎn)生undo log,其中記錄的是老版本的數(shù)據(jù),當(dāng)舊事務(wù)需要讀取數(shù)據(jù)時(shí),可以順著undo鏈找到滿足其可見性地記錄。
  • undo log通常以邏輯日志的形式存在。我們可以認(rèn)為當(dāng)delete一條記錄時(shí),undo log會(huì)產(chǎn)生一條對應(yīng)的insert記錄,反之亦然。當(dāng)update一條記錄時(shí),會(huì)產(chǎn)生一條相反的update記錄。
  • undo log采用段segment的方式來記錄,每個(gè)undo操作在記錄的時(shí)候占用一個(gè)undo log segment。
  • undo log也會(huì)產(chǎn)生redo log,因?yàn)閡ndo log也要實(shí)現(xiàn)持久性保護(hù)。

undo log通常以邏輯日志的形式存在。我們可以認(rèn)為當(dāng)delete一條記錄時(shí),undo log會(huì)產(chǎn)生一條對應(yīng)的insert記錄,反之亦然。當(dāng)update一條記錄時(shí),會(huì)產(chǎn)生一條相反的update記錄。

undo log采用段segment的方式來記錄,每個(gè)undo操作在記錄的時(shí)候占用一個(gè)undo log segment。

undo log也會(huì)產(chǎn)生redo log,因?yàn)閡ndo log也要實(shí)現(xiàn)持久性保護(hù)。

2.2 undo log segment

為了保證事務(wù)并發(fā)操作時(shí),寫各自的undo log時(shí)不發(fā)生沖突,nnodb用段的方式管理undo log。rollback segment稱為回滾段,每個(gè)回滾段中有1024個(gè)undo log segment。MySQL5.5以后的版本支持128個(gè)rollback segment,就可以存儲(chǔ)128*1024個(gè)操作,還可以通過innodb_undo_logs參數(shù)定義盯梢個(gè)rollback segment。

2.3 purge

在聚集索引列的操作中,MySQL是這樣設(shè)計(jì)的。對一條delete語句

delete from t where a = 1

假如a有聚集索引(主鍵),那么不會(huì)進(jìn)行真正的刪除,而是在主鍵列等于1的記錄處設(shè)置delete flag為1,即把記錄保存在B+樹中。同理,對于update操作,不是直接更新記錄,而是把舊紀(jì)錄標(biāo)識為刪除,再創(chuàng)建一條新記錄。

那么,舊版本記錄什么時(shí)候真正的刪除呢?

InnoDB使用undo日志進(jìn)行舊版本的刪除操作,這個(gè)操作稱為purge操作。InnoDB開辟了purge線程進(jìn)行purge操作,并且可以控制purge線程的數(shù)量,每個(gè)purge線程每10s 進(jìn)行一次purge操作。

InnoDB的undo log設(shè)計(jì)

一個(gè)頁上允許多個(gè)事務(wù)的undo log存在,undo log的存儲(chǔ)順序是隨時(shí)的。InnoDB維護(hù)了一個(gè)history鏈表,按照事務(wù)提交的順序?qū)ndo log進(jìn)行連接。

在執(zhí)行purge過程中,InnoDB存儲(chǔ)引擎首先從history list中找到第一個(gè)需要被清理的記錄,這里為trx1,清理之后InnoDB存儲(chǔ)引擎會(huì)在trx1所在的Undo page中繼續(xù)尋找是否存在可以被清理的記錄,這里會(huì)找到事務(wù)trx3,接著找到trx5,但是發(fā)現(xiàn)trx5被其他事務(wù)所引用而不能清理,故再去history list中取查找,發(fā)現(xiàn)最尾端的記錄時(shí)trx2,接著找到trx2所在的Undo page,依次把trx6、trx4清理,由于Undo page2中所有的記錄都被清理了,因此該Undo page可以進(jìn)行重用。

InnoDB存儲(chǔ)引擎這種先從history list中找undo log,然后再從Undo page中找undo log的設(shè)計(jì)模式是為了避免大量隨機(jī)讀操作,從而提高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ā)后,會(huì)將內(nèi)存中的臟數(shù)據(jù)刷到磁盤。

innodb存儲(chǔ)引擎中checkpoint分為兩種:

  • sharp checkpoint:在重用redo log文件(例如切換日志文件)的時(shí)候,將所有已記錄到redo log中對應(yīng)的臟數(shù)據(jù)刷到磁盤。
  • fuzzy checkpoint:一次只刷一小部分的日志到磁盤,而非將所有臟日志刷盤。有以下幾種情況會(huì)觸發(fā)該檢查點(diǎn):

master thread checkpoint。由master線程控制,每秒或每10秒刷入一定比例的臟頁到磁盤。
flush_lru_list checkpoint。從MySQL5.6開始可通過 innodb_page_cleaners 變量指定專門負(fù)責(zé)臟頁刷盤的page cleaner線程的個(gè)數(shù),該線程的目的是為了保證lru列表有可用的空閑頁。
async/sync flush checkpoint。同步刷盤還是異步刷盤。例如還有非常多的臟頁沒刷到磁盤(非常多是多少,有比例控制),這時(shí)候會(huì)選擇同步刷到磁盤,但這很少出現(xiàn);如果臟頁不是很多,可以選擇異步刷到磁盤,如果臟頁很少,可以暫時(shí)不刷臟頁到磁盤
dirty page too much checkpoint。臟頁太多時(shí)強(qiáng)制觸發(fā)檢查點(diǎn),目的是為了保證緩存有足夠的空閑空間。too much的比例由變量 innodb_max_dirty_pages_pct 控制,MySQL 5.6默認(rèn)的值為75,即當(dāng)臟頁占緩沖池的百分之75后,就強(qiáng)制刷一部分臟頁到磁盤。

由于刷臟頁需要一定的時(shí)間來完成,所以記錄檢查點(diǎn)的位置是在每次刷盤結(jié)束之后才在redo log中標(biāo)記的。

3.2 LSN

3.2.1 LSN概念

LSN稱為日志的邏輯序列號,在InnoDB中占用8個(gè)字節(jié)

我們可以通過LSN了解到下面這些信息:

  • 數(shù)據(jù)頁的版本信息。
  • 寫入的日志總量。
  • 檢查點(diǎn)的位置。

在下面兩個(gè)位置存在LSN:

  • redo log的記錄中。
  • 每個(gè)數(shù)據(jù)頁的頭部有一個(gè)變量fil_page_lsn記錄了本頁最終的LSN值是多少。

顯然,如果頁中的LSN值小于redo log中的LSN值,說明數(shù)據(jù)出現(xiàn)了丟失。

通過show engine innodb status可以查看當(dāng)前InnoDB的運(yùn)行信息,其中有一欄log中有關(guān)于lsn的記錄。

  • 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是上一次檢查點(diǎn)所在位置的LSN。

3.2.2 LSN處理流程

(1).首先修改內(nèi)存中的數(shù)據(jù)頁,并在數(shù)據(jù)頁中記錄LSN,暫且稱之為data_in_buffer_lsn;

(2).并且在修改數(shù)據(jù)頁的同時(shí)(幾乎是同時(shí))向redo log in buffer中寫入redo log,并記錄下對應(yīng)的LSN,暫且稱之為redo_log_in_buffer_lsn;

(3).寫完buffer中的日志后,當(dāng)觸發(fā)了日志刷盤的幾種規(guī)則時(shí),會(huì)向redo log file on disk刷入重做日志,并在該文件中記下對應(yīng)的LSN,暫且稱之為redo_log_on_disk_lsn;

(4).數(shù)據(jù)頁不可能永遠(yuǎn)只停留在內(nèi)存中,在某些情況下,會(huì)觸發(fā)checkpoint來將內(nèi)存中的臟頁(數(shù)據(jù)臟頁和日志臟頁)刷到磁盤,所以會(huì)在本次checkpoint臟頁刷盤結(jié)束時(shí),在redo log中記錄checkpoint的LSN位置,暫且稱之為checkpoint_lsn。

(5).要記錄checkpoint所在位置很快,只需簡單的設(shè)置一個(gè)標(biāo)志即可,但是刷數(shù)據(jù)頁并不一定很快,例如這一次checkpoint要刷入的數(shù)據(jù)頁非常多。也就是說要刷入所有的數(shù)據(jù)頁需要一定的時(shí)間來完成,中途刷入的每個(gè)數(shù)據(jù)頁都會(huì)記下當(dāng)前頁所在的LSN,暫且稱之為data_page_on_disk_lsn。

上圖中,從上到下的橫線分別代表:時(shí)間軸、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)以及檢查點(diǎn)記錄的LSN(checkpoint_lsn)。

假設(shè)在最初時(shí)(12:0:00)所有的日志頁和數(shù)據(jù)頁都完成了刷盤,也記錄好了檢查點(diǎn)的LSN,這時(shí)它們的LSN都是完全一致的。

假設(shè)此時(shí)開啟了一個(gè)事務(wù),并立刻執(zhí)行了一個(gè)update操作,執(zhí)行完成后,buffer中的數(shù)據(jù)頁和redo log都記錄好了更新后的LSN值,假設(shè)為110。這時(shí)候如果執(zhí)行 show engine innodb status 查看各LSN的值,即圖中①處的位置狀態(tài),結(jié)果會(huì)是:

log sequence number(110) > log flushed up to(100) = pages flushed up to = last checkpoint at

之后又執(zhí)行了一個(gè)delete語句,LSN增長到150。等到12:00:01時(shí),觸發(fā)redo log刷盤的規(guī)則(其中有一個(gè)規(guī)則是 innodb_flush_log_at_timeout 控制的默認(rèn)日志刷盤頻率為1秒),這時(shí)redo log file on disk中的LSN會(huì)更新到和redo log in buffer的LSN一樣,所以都等于150,這時(shí) show engine innodb status ,即圖中②的位置,結(jié)果將會(huì)是:

log sequence number(150) = log flushed up to > pages flushed up to(100) = last checkpoint at

再之后,執(zhí)行了一個(gè)update語句,緩存中的LSN將增長到300,即圖中③的位置。

假設(shè)隨后檢查點(diǎn)出現(xiàn),即圖中④的位置,正如前面所說,檢查點(diǎn)會(huì)觸發(fā)數(shù)據(jù)頁和日志頁刷盤,但需要一定的時(shí)間來完成,所以在數(shù)據(jù)頁刷盤還未完成時(shí),檢查點(diǎn)的LSN還是上一次檢查點(diǎn)的LSN,但此時(shí)磁盤上數(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的大小無法確定,因?yàn)槿罩舅⒈P可能快于數(shù)據(jù)刷盤,也可能等于,還可能是慢于。但是checkpoint機(jī)制有保護(hù)數(shù)據(jù)刷盤速度是慢于日志刷盤的:當(dāng)數(shù)據(jù)刷盤速度超過日志刷盤時(shí),將會(huì)暫時(shí)停止數(shù)據(jù)刷盤,等待日志刷盤進(jìn)度超過數(shù)據(jù)刷盤。

等到數(shù)據(jù)頁和日志頁刷盤完畢,即到了位置⑤的時(shí)候,所有的LSN都等于300。

隨著時(shí)間的推移到了12:00:02,即圖中位置⑥,又觸發(fā)了日志刷盤的規(guī)則,但此時(shí)buffer中的日志LSN和磁盤中的日志LSN是一致的,所以不執(zhí)行日志刷盤,即此時(shí) show engine innodb status 時(shí)各種lsn都相等。

隨后執(zhí)行了一個(gè)insert語句,假設(shè)buffer中的LSN增長到了800,即圖中位置⑦。此時(shí)各種LSN的大小和位置①時(shí)一樣。

隨后執(zhí)行了提交動(dòng)作,即位置⑧。默認(rèn)情況下,提交動(dòng)作會(huì)觸發(fā)日志刷盤,但不會(huì)觸發(fā)數(shù)據(jù)刷盤,所以 show engine innodb status 的結(jié)果是:

log sequence number = log flushed up to > pages flushed up to = last checkpoint at

最后隨著時(shí)間的推移,檢查點(diǎn)再次出現(xiàn),即圖中位置⑨。但是這次檢查點(diǎn)不會(huì)觸發(fā)日志刷盤,因?yàn)槿罩镜腖SN在檢查點(diǎn)出現(xiàn)之前已經(jīng)同步了。假設(shè)這次數(shù)據(jù)刷盤速度極快,快到一瞬間內(nèi)完成而無法捕捉到狀態(tài)的變化,這時(shí) show engine innodb status 的結(jié)果將是各種LSN相等。

3.3 InnoDB的恢復(fù)行為

啟動(dòng)InnoDB時(shí),一定會(huì)進(jìn)行恢復(fù)操作,無論上次是因?yàn)槭裁丛蛲顺觥?/p>

checkpoint表示已經(jīng)完整刷到磁盤上data page上的LSN,因此恢復(fù)時(shí)僅需要恢復(fù)從checkpoint開始的日志部分。例如,當(dāng)數(shù)據(jù)庫在上一次checkpoint的LSN為10000時(shí)宕機(jī),且事務(wù)是已經(jīng)提交過的狀態(tài)。啟動(dòng)數(shù)據(jù)庫時(shí)會(huì)檢查磁盤中數(shù)據(jù)頁的LSN,如果數(shù)據(jù)頁的LSN小于日志中的LSN,則會(huì)從檢查點(diǎn)開始恢復(fù)。

還有一種情況,在宕機(jī)前正處于checkpoint的刷盤過程,且數(shù)據(jù)頁的刷盤進(jìn)度超過了日志頁的刷盤進(jìn)度。這時(shí)候一宕機(jī),數(shù)據(jù)頁中記錄的LSN就會(huì)大于日志頁中的LSN,在重啟的恢復(fù)過程中會(huì)檢查到這一情況,這時(shí)超出日志進(jìn)度的部分將不會(huì)重做,因?yàn)檫@本身就表示已經(jīng)做過的事情,無需再重做。

另外,事務(wù)日志具有冪等性,所以多次操作得到同一結(jié)果的行為在日志中只記錄一次。而二進(jìn)制日志不具有冪等性,多次操作會(huì)全部記錄下來,在恢復(fù)的時(shí)候會(huì)多次執(zhí)行二進(jìn)制日志中的記錄,速度就慢得多。例如,某記錄中id初始值為2,通過update將值設(shè)置為了3,后來又設(shè)置成了2,在事務(wù)日志中記錄的將是無變化的頁,根本無需恢復(fù);而二進(jìn)制會(huì)記錄下兩次update操作,恢復(fù)時(shí)也將執(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ū)別淺析

標(biāo)簽:崇左 蘭州 黃山 銅川 湘潭 仙桃 衡水 湖南

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《MySQL中的redo log和undo log日志詳解》,本文關(guān)鍵詞  ;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問題,煩請?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無關(guān)。
  • 相關(guān)文章
  • 收縮
    • 微信客服
    • 微信二維碼
    • 電話咨詢

    • 400-1100-266
    莎车县| 工布江达县| 泾阳县| 夏邑县| 南平市| 驻马店市| 龙州县| 梧州市| 九江市| 天祝| 祁阳县| 云和县| 清苑县| 西华县| 聂拉木县| 会昌县| 昌平区| 阜南县| 永仁县| 南木林县| 辽源市| 宜春市| 麻江县| 大荔县| 班玛县| 吴堡县| 运城市| 万荣县| 措勤县| 丹寨县| 阳江市| 方正县| 唐海县| 江川县| 津市市| 宁波市| 青龙| 安平县| 菏泽市| 健康| 常熟市|