MySQL redo
標籤:MySQL
標籤:MySQL結構
標籤:MySQL redo
Redo log 相關結構和記入過程:
1. log_sys物件
(1). log_sys作用
log_sys是redo日誌系統的關鍵全域性變數。負責三項事情:
log record從mtr_buf複製到logbuffer:
使用者執行緒mtr_commit時,將mtr_buf中的redo record,複製到log buffer中;
log record從log buffer複製到logfile:write_mutex控制logbuffer順序的刷盤;
dirty page從buffer pool刷寫到datafile:log_flush_order_mutex控制flushlist的順序刷盤;執行CHECKPOINT或preflush。
其中,三個鎖分別控制
log_t互斥訪問(mutex)、
日誌刷盤(write_mutex)、
資料刷盤(log_flush_order_mutex)的操作。
(2). 與日誌組、日誌相關變數
ulint n_files:Ib_logfile的檔案個數。
lsn_t file_size:檔案大小。
ulint space_id:Redo log 的space id, 固定大小,值為SRV_LOG_SPACE_FIRST_ID。
ulint state:LOG_GROUP_OK 或者 LOG_GROUP_CORRUPTED。
lsn_t lsn:該group內寫到的lsn。
lsn_t lsn_offset:上述lsn對應的檔案偏移量。在log_group_t裡的lsn和lsn_offset欄位已經記錄了某個日誌lsn和其存放在檔案內的偏移量之間的對應關係。
計算時需要結合LOG_FILE_START_LSN,判斷是否跨了logfile檔案。
byte** file_header_bufs:
Buffer區域,用於設定日誌檔案頭資訊,並寫入ib logfile。當切換到新的ib_logfile時,更新該檔案的起始lsn,寫入頭部。
頭部資訊還包含: LOG_GROUP_ID, LOG_FILE_START_LSN(當前檔案起始lsn)、LOG_FILE_WAS_CREATED_BY_HOT_BACKUP(函式log_group_file_header_flush)。
lsn_t scanned_lsn:用於崩潰恢復時輔助記錄掃描到的lsn號。
byte* checkpoint_buf:
Checkpoint緩衝區,用於向日志檔案寫入checkpoint資訊(下文詳細描述)。
只有做完checkpoint後,其之前的日誌才可以不再保留,否則系統崩潰時則無法恢復。在系統崩潰後的恢復,需要從checkpoint點開始。
但我們需要把checkpoint的相關資訊持久化的儲存下來,才能在系統崩潰時不會丟失這些檢查點相關的資訊。Checkpoint相關的資訊只存放在ib _logfile0中。
lsn_t write_lsn:最近一次完成寫入到檔案的LSN。
lsn_t current_flush_lsn:當前正在fsync到的LSN。
lsn_t flushed_to_disk_lsn:最近一次完成fsync到檔案的LSN。
innodb_log_files_in_group=4
innodb_log_file_size=4G
總檔案大小: 17179869184
log_sys->max_modified_age_async = 12175607164 (71%)
log_sys->max_modified_age_sync = 13045293390 (76%)
log_sys->max_checkpoint_age_async = 13480136503 (78%)
log_sys->max_checkpoint_age = 13914979615 (81%)
噹噹前未刷髒的最老lsn和當前lsn的距離超過max_modified_age_async(71%)時,且開啟了選項innodb_adaptive_flushing時,page cleaner執行緒會去嘗試做更多的dirty page flush工作,避免髒頁堆積。
噹噹前未刷髒的最老lsn和當前Lsn的距離超過max_modified_age_sync(76%)時,使用者執行緒需要去做同步刷髒,這是一個效能下降的臨界點,會極大的影響整體吞吐量和響應時間。
當上次checkpoint的lsn和當前lsn超過max_checkpoint_age(81%),使用者執行緒需要同步地做一次checkpoint,需要等待checkpoint寫入完成。
當上次checkpoint的lsn和當前lsn的距離超過max_checkpoint_age_async(78%)但小於max_checkpoint_age(81%)時,使用者執行緒做一次非同步checkpoint(後臺非同步執行緒執行CHECKPOINT資訊寫入操作),無需等待checkpoint完成。
(3). 與redo log 記憶體緩衝區相關的成員變數:
變數名描述
ulint buf_size:Log buffer 大小,受引數innodb_log_buffer_size控制,但可能會自動extend。
byte* buf:Log buffer起始位置指標。
ulint buf_free:Log buffer中當前空閒可寫的位置。redo record可複製寫入的buffer的位置(buf_free)。
ulint max_buf_free:值為log_sys->buf_size / LOG_BUF_FLUSH_RATIO - LOG_BUF_FLUSH_MARGIN.
其中: LOG_BUF_FLUSH_RATIO=2, LOG_BUF_FLUSH_MARGIN=(4 * 512 + 4* page_size),page_size預設為16k.
當buf_free超過該值時,可能觸發使用者執行緒去寫redo;在事務拷redo record到buffer後,也會判斷該值,如果超過buf_free,設定log_sys->check_flush_or_checkpoint為true。
ulint buf_next_to_write:Log buffer偏移量,下次寫入redo log檔案的起始位置,即本次寫入的結束位置。redo緩衝區將要向磁碟刷盤的位置(buf_next_to_write)
volatile bool is_extending:Log buffer是否正在進行擴充套件 (防止過大的redo log entry無法寫入buffer), 實際上,當寫入的redo log長度超過buf_size/2時,就會去呼叫函式log_buffer_extend,一旦擴充套件Buffer,就不會在縮減回去了!
ulint write_end_offset:本次寫入的結束位置偏移量(從邏輯來看有點多餘,直接用log_sys->buf_free就行了)。
(4). 和Checkpoint檢查點相關的成員變數:
變數名描述
ib_uint64_t next_checkpoint_no:每完成一次checkpoint遞增該值。
lsn_t last_checkpoint_lsn:最近一次checkpoint時的lsn,每完成一次checkpoint,將next_checkpoint_lsn的值賦給last_checkpoint_lsn。
lsn_t next_checkpoint_lsn:下次checkpoint的lsn(本次發起的checkpoint的lsn)。
mtr_buf_t* append_on_checkpoint:
5.7新增,在做DDL時(例如增刪列),會先將包含MLOG_FILE_RENAME2日誌記錄的buf掛到這個變數上。
在DDL完成後,再清理掉。(log_append_on_checkpoint),主要是防止DDL期間crash產生的資料詞典不一致。
ulint n_pending_checkpoint_writes:大於0時,表示有一個checkpoint寫入操作正在進行。使用者發起checkpoint時,遞增該值。後臺執行緒完成checkpoint寫入後,遞減該值(log_io_complete)
rw_lock_t checkpoint_lock:checkpoint鎖,每次寫checkpoint資訊時需要加x鎖。由非同步io執行緒釋放該x鎖
byte* checkpoint_buf:Checkpoint資訊緩衝區,每次checkpoint前,先寫該buf,再將buf刷到磁碟。
2. 日誌檔案頭
每個日誌檔案的前2048位元組是存放的檔案頭資訊。
其中幾個重要的欄位在這裡加以說明:
日誌檔案頭共佔用4個OS_FILE_LOG_BLOCK_SIZE的大小,即2KB。這裡對部分欄位做簡要介紹:
1) LOG_GROUP_ID 這個log檔案所屬的日誌組,佔用4個位元組,當前都是0;
2) LOG_FILE_START_LSN 這個log檔案記錄的開始日誌的lsn,佔用8個位元組;
3) LOG_FILE_WAS_CRATED_BY_HOT_BACKUP 備份程式所佔用的位元組數,共佔用32位元組;
4) LOG_CHECKPOINT_1/LOG_CHECKPOINT_2 兩個記錄InnoDB checkpoint資訊的欄位,分別從檔案頭的第二個和第四個block開始記錄,只使用日誌檔案組的第一個日誌檔案。
從地址2KB偏移量開始,其後就是順序寫入的各個日誌塊(log block)。
3. 日誌塊
日誌塊結構
所有的redo日誌記錄是以日誌塊為單位組織在一起的,日誌塊的大小為OS_FILE_LOG_BLOCK_SIZE(預設值為512位元組),所有的日誌記錄以日誌塊為單位順序寫入日誌檔案。
每一條記錄都有自己的LSN(log sequence number, 表示從日誌記錄建立開始到特定的日誌記錄已經寫入的位元組數)。
每個日誌塊包含一個日誌頭段(12位元組)、一個尾段(4位元組),以及一組日誌記錄(512 – 12 – 4 = 496位元組) 。
二、 日誌寫入簡要過程
1. mtr log生成
2. 從mtr_buf到logbuffer
複製mtr中的record
unsigned long buf_free:buf中空閒的第一個位置,按照該位置向logbuffer中複製record。
max_buf_free:推薦的buf_free的最大值,會判斷該值,如果超過max_buf_free,需要刷logbuffer;並設定log_sys->check_flush_or_checkpoint為true。
3. 從log buffer到logfile
日誌的刷盤是透過呼叫void log_write_up_to( lsn_t lsn, bool flush_to_disk),如果flush_to_disk為True,則表示將引數lsn之前日誌都write&flush;同時更新相應偏移量。
三、 mtr
1. Mini transaction
Mini transaction(簡稱mtr)是InnoDB對物理資料檔案操作的最小事務單元,用於管理對Page加鎖、修改、釋放、以及日誌提交到公共buffer等工作。
一個mtr操作必須是原子的,一個事務可以包含多個mtr。每個mtr完成後需要將本地產生的日誌複製到公共緩衝區,將修改的髒頁放到flush list上。
2. mtr_t結構
struct mtr_t {
/* mtr_t 內嵌一個結構體Impl. */
struct Impl {
mtr_buf_t m_memo; /* 一個被加鎖的物件以及它加的鎖. */
mtr_buf_t m_log; /* mini-transaction 的 log. */
bool m_made_dirty; /* 是否修改了Buffer Pool中的Page為髒頁. */
bool m_modifications; /* 是否修改了Buffer Pool中的Page. */
ib_uint32_t m_n_log_recs; /* 該 mini-transaction 包含多少條log. */
mtr_log_t m_log_mode; /* mini-transaction 的工作模式(MTR_LOG_ALL, MTR_LOG_NO_REDO,MTR_LOG_NONE).
Mtr的工作模式,包括四種:
MTR_LOG_ALL:預設模式,記錄所有會修改磁碟資料的操作;
MTR_LOG_NONE:不記錄redo,髒頁也不放到flush list上;
MTR_LOG_NO_REDO:不記錄redo,但髒頁放到flush list上;
MTR_LOG_SHORT_INSERTS:插入記錄操作REDO,在將記錄從一個page複製到另外一個新建的page時用到,此時忽略寫索引資訊到redo log中。*/
mtr_state_t m_state; /* mini-transaction 的狀態: MTR_STATE_INIT, MTR_STATE_ACTIVE, MTR_STATE_COMMITTING, MTR_STATE_COMMITTED. */
}
}
3. mtr_t中成員memo
是個latch持有狀態的陣列列表,採用的是dyn_array_t的動態記憶體結構來儲存的,每個單元儲存的是 mtr_memo_slot_t 。
m_memo中元素是mtr_memo_slot_t, 記錄加鎖的物件和加鎖的型別.
object是latch的物件,可以是rw_lock_t物件,也可以是buf_block_t物件。
/** mini-transaction memo stack slot. */
struct mtr_memo_slot_t {
void *object; /* 加鎖的物件. */
ulint type; /* 持有的鎖型別,W or R. */
};
4. mt_t中的成員log
是也是一個dyn_array_t動態結構的記憶體,用來儲存mtr產生的日誌資訊。日誌的寫入是透過mtr0log.h來寫入的。
重做日誌雖然有很多種型別,但重做的日誌格式是統一的,日誌格式是有日誌頭和日誌體組成。
日誌頭資訊是由type、space和page no組成,由mlog_write_initial_log_record_fast函式寫入到mtr_t的log中的。
log body的資料寫入是透過mtr0log.h中的日誌寫入方法進行寫入的,每寫入一條操作日誌,n_log_recs會加1。
5. mini-transaction 具體流程
mtr_t mtr
mtr.start()
/* ... */
/* 寫入資料至 mini-transaction 的 m_log. */
/* ... */
mtr.commit()
(1). mini-transaction 的start()
/** 啟動一個 mini-transaction. */
@param synctrue if it is a synchronous mini-transaction
@param read_onlytrue if read only mini-transaction */
void mtr_t::start(bool sync, bool read_only) {
UNIV_MEM_INVALID(this, sizeof(*this));
UNIV_MEM_INVALID(&m_impl, sizeof(m_impl));
m_sync = sync;
m_commit_lsn = 0;
new (&m_impl.m_log) mtr_buf_t(); /* 記錄Redo log的mtr本地Buffer. */
new (&m_impl.m_memo) mtr_buf_t();
/* 初始化 mini-transaction 欄位. */
m_impl.m_mtr = this;
m_impl.m_log_mode = MTR_LOG_ALL;
m_impl.m_inside_ibuf = false;
m_impl.m_modifications = false;
m_impl.m_made_dirty = false;
m_impl.m_n_log_recs = 0;
m_impl.m_state = MTR_STATE_ACTIVE;
m_impl.m_flush_observer = NULL;
ut_d(m_impl.m_magic_n = MTR_MAGIC_N);
}
(2). 鎖的處理
不同的 mini-transaction 如何互斥?
在運算元據前,會根據鎖型別,加不同型別的鎖,之後將object和鎖型別存入m_memo:
mtr_memo_push(mtr, object, type);
commit完成之後呼叫release_latches(RELEASE_ALL)將資料上的鎖釋放.
(3). mini-transaction 插入資料
byte *mlog_open(mtr_t *mtr, ulint size): 開啟mtr的m_log
mlog_write_initial_log_record_low()函式向m_log中寫入type,space id,page no,並增加m_n_log_recs的數量
mtr->get_log()->push()按不同的型別寫資料
mlog_close(): 更新m_log中的位置
(4). mini-transaction 的 commit 過程
commit過程將mini-transaction的m_log資料複製到Redo Log Buffer中.
將m_state設定為MTR_STATE_COMMITTING後,呼叫mtr_t::Command::execute()。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/25380026/viewspace-2747128/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- MySQL:Redo & binlogMySql
- mysql之 redo logMySql
- MySQL 日誌 undo | redoMySql
- 深入理解MYSQL undo redoMySql
- MySQL重做日誌(redo log)MySql
- MySQL redo log最佳化MySql
- MySQL的Redo log 以及Bin logMySql
- MySQL redo與undo日誌解析MySql
- MySQL中的redo log和checkpointMySql
- MySQL之事務和redo日誌MySql
- MySQL中的redo log和undo logMySql
- MySQL binlog和redo的組提交MySql
- MySQL Undo Log和Redo Log介紹MySql
- MySQL 日誌系統 redo log、binlogMySql
- MySQL學習之change buffer 和 redo logMySql
- MySQL如何計算統計redo log大小MySql
- 淺析MySQL事務中的redo與undoMySql
- MySQL Redo log頁內邏輯怎麼理解MySql
- 【Mysql】三大日誌 redo log、bin log、undo logMySql
- MySQL 5.6修改REDO日誌的大小和個數MySql
- 【REDO】Oracle redo undo 學習Oracle Redo
- 【REDO】Oracle redo內部結構Oracle Redo
- 【REDO】Oracle redo advice-sqlOracle RedoSQL
- MySQL中Redo Log相關的重要引數總結MySql
- 【MySQL】undo,redo,2PC,恢復思維導圖MySql
- Redo Log之一:理解Oracle redo logOracle Redo
- mysql日誌:redo log、binlog、undo log 區別與作用MySql
- MySQL中redo log、undo log、binlog關係以及區別MySql
- 深入理解MySQL系列之redo log、undo log和binlogMySql
- MySQL更新資料時,日誌(redo log、binlog)執行流程MySql
- 基於Redo Log和Undo Log的MySQL崩潰恢復流程MySql
- mysql關於redo事務日誌ib_logfile的理解MySql
- 一生摯友redo log、binlog《死磕MySQL系列 二》MySql
- 資料庫篇:mysql日誌型別之 redo、undo、binlog資料庫MySql型別
- MYSQL 是如何保證binlog 和redo log同時提交的?MySql
- Oracle Redo and UndoOracle Redo
- Redo的原理
- Oracle redo解析之-1、oracle redo log結構計算Oracle Redo