1,mtr0log.h
這個頭文件裏面聲明瞭寫相關日誌和解析相關日誌的函數,還有打開和關閉mtr日誌buffer的函數。
2,mtr0log.ic
基本的幾種日誌類型格式
MLOG_XBYTE:type(1字節),space id(1-5字節,原本是4字節,由於壓縮的原因),page no,offset(頁內偏移),data(實際數據)
前面type,space id,page no是redo日誌通用的,後面就是redo日誌的body。
MLOG_STRING:type,space id,page no,offset,len(字符串實際長度),data(記錄的字符串)
3,mtr0log.cc
關鍵相關函數
//打開日誌buffer
byte *mlog_open(mtr_t *mtr, ulint size) {
mtr->set_modified();
//日誌模式爲這兩種,不記錄日誌
if (mtr_get_log_mode(mtr) == MTR_LOG_NONE ||
mtr_get_log_mode(mtr) == MTR_LOG_NO_REDO) {
return (NULL);
}
//open函數:當前該mtr的buffer的最後一個block是否能容納size大小空間,如果可以返回空閒區域的地址
//,如果不行,再添加一個block到buffer鏈表的末端,返回block最後一個block地址。也就是說返回的地址
//是需要繼續寫日誌的位置。
return (mtr->get_log()->open(size));
}
//關閉該mtr的日誌buffer
void mlog_close(mtr_t *mtr, /*!< in: mtr */
byte *ptr) /*!< in: buffer space from ptr up was not used */
{
ut_ad(mtr_get_log_mode(mtr) != MTR_LOG_NONE);
ut_ad(mtr_get_log_mode(mtr) != MTR_LOG_NO_REDO);
//該函數表示寫完了,記錄該mtr寫了多少字節的日誌
mtr->get_log()->close(ptr);
}
//寫入redo日誌的通用部分
byte *mlog_write_initial_log_record_fast(
const byte *ptr, /*!< in: pointer to (inside) a buffer
frame holding the file page where
modification is made */
mlog_id_t type, /*!< in: log item type: MLOG_1BYTE, ... */
byte *log_ptr, /*!< in: pointer to mtr log which has
been opened */
mtr_t *mtr) /*!< in/out: mtr */
{
const byte *page;
space_id_t space;
page_no_t offset;
ut_ad(log_ptr);
ut_d(mtr->memo_modify_page(ptr));
//ptr指向在內存buffer中,一個page中要修改的地方,向下對頁面大小取模,就能夠得到該page的首地址
page = (const byte *)ut_align_down(ptr, UNIV_PAGE_SIZE);
//讀取表空間號
space = mach_read_from_4(page + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID);
//讀取頁面號
offset = mach_read_from_4(page + FIL_PAGE_OFFSET);
//...............
return (mlog_write_initial_log_record_low(type, space, offset, log_ptr, mtr));
}
byte *mlog_write_initial_log_record_low(mlog_id_t type, space_id_t space_id,
page_no_t page_no, byte *log_ptr,
mtr_t *mtr) {
ut_ad(type <= MLOG_BIGGEST_TYPE);
//寫入一條日誌的類型
mach_write_to_1(log_ptr, type);
log_ptr++;
//將表空間號和頁面號壓縮,並且寫入日誌指針後面
log_ptr += mach_write_compressed(log_ptr, space_id);
log_ptr += mach_write_compressed(log_ptr, page_no);
//該mtr中記錄數加1
mtr->added_rec();
return (log_ptr);
}
//一個簡單的壓縮算法
//大概思想就是:最高位爲0的時候,只用1字節,也就是8位,最高位浪費掉,用來標識有多少位實際用來存
//儲原本需要4字節的數據,也就是隻用7位來存儲。存儲的範圍也應該小於7位所能存儲的最大值。
//次高位爲0的時候,說明有兩位浪費了,只用14位存儲。
//從左到右,第三位爲0的時候,說明浪費了3位,只用了21位存儲數據。
ulint mach_write_compressed(byte *b, ulint n) {
ut_ad(b);
if (n < 0x80) {
/* 0nnnnnnn (7 bits) */
mach_write_to_1(b, n);
return (1);
} else if (n < 0x4000) {
/* 10nnnnnn nnnnnnnn (14 bits) */
mach_write_to_2(b, n | 0x8000);
return (2);
} else if (n < 0x200000) {
/* 110nnnnn nnnnnnnn nnnnnnnn (21 bits) */
mach_write_to_3(b, n | 0xC00000);
return (3);
} else if (n < 0x10000000) {
/* 1110nnnn nnnnnnnn nnnnnnnn nnnnnnnn (28 bits) */
mach_write_to_4(b, n | 0xE0000000);
return (4);
} else {
/* 11110000 nnnnnnnn nnnnnnnn nnnnnnnn nnnnnnnn (32 bits) */
mach_write_to_1(b, 0xF0);
mach_write_to_4(b + 1, n);
return (5);
}
}
//解析日誌通用部分,也就是解析出日誌類型,space id,頁面號,並且返回body部分
//該函數用於崩潰恢復,返回NULL說明日誌不完全
byte *mlog_parse_initial_log_record(
const byte *ptr, /*!< in: buffer */
const byte *end_ptr, /*!< in: buffer end */
mlog_id_t *type, /*!< out: log record type: MLOG_1BYTE, ... */
space_id_t *space, /*!< out: space id */
page_no_t *page_no) /*!< out: page number */
{
if (end_ptr < ptr + 1) {
return (NULL);
}
//MLOG_SINGLE_REC_FLAG是mtr單日誌記錄的標誌,如果是多日誌記錄,那麼還需要額外記錄一條redo日誌
//,說明該mtr結束了。因此,將最高位去掉,就是type
*type = (mlog_id_t)((ulint)*ptr & ~MLOG_SINGLE_REC_FLAG);
ut_ad(*type <= MLOG_BIGGEST_TYPE);
ptr++;
//還需要至少兩字節,不然是不完整的
if (end_ptr < ptr + 2) {
return (NULL);
}
//解壓,與壓縮原理一樣
*space = mach_parse_compressed(&ptr, end_ptr);
//page 號
if (ptr != NULL) {
*page_no = mach_parse_compressed(&ptr, end_ptr);
}
//返回body
return (const_cast<byte *>(ptr));
}