typedef struct dulint_struct { ulint high; /* most significant 32 bits */ ulint low; /* least significant 32 bits */ }dulint_t;LSN真正的含義是儲存引擎向重做日志系統寫入的日志量(字節數),這個日志量包括寫入的日志字節 + block_header_size + block_tailer_size。LSN的初始化值是:LOG_START_LSN(相當於8192),在調用日志寫入函數LSN就一直隨著寫入的日志長度增加,具體看:
void log_write_low(byte* str, ulint str_len) { log_t* log = log_sys; . . . part_loop: /*計算part length*/ data_len = log->buf_free % OS_FILE_LOG_BLOCK_SIZE + str_len; . . . /*將日志內容拷貝到log buffer*/ ut_memcpy(log->buf + log->buf_free, str, len); str_len -= len; str = str + len; . . . if(data_len = OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_TRL_SIZE){ /*完成一個block的寫入*/ . . . len += LOG_BLOCK_HDR_SIZE + LOG_BLOCK_TRL_SIZE; log->lsn = ut_dulint_add(log->lsn, len); . . . } else /*更改lsn*/ log->lsn = ut_dulint_add(log->lsn, len); . . . }
LSN是不會減小的,它是日志位置的唯一標記。在重做日志寫入、checkpoint構建和PAGE頭裡面都有LSN。
關於日志寫入:
例如當前重做日志的LSN = 2048,這時候innodb調用log_write_low寫入一個長度為700的日志,2048剛好是4個block長度,那麼需要存儲700長度的日志,需要量個BLOCK(單個block只能存496個字節)。那麼很容易得出新的LSN = 2048 + 700 + 2 * LOG_BLOCK_HDR_SIZE(12) + LOG_BLOCK_TRL_SIZE(4) = 2776。
關於checkpoint和日志恢復:
在page的fil_header中的LSN是表示最後刷新是的LSN, 假如數據庫中存在PAGE1 LSN = 1024,PAGE2 LSN = 2048, 系統重啟時,檢測到最後的checkpoint LSN = 1024,那麼系統在檢測到PAGE1不會對PAGE1進行恢復重做,當系統檢測到PAGE2的時候,會將PAGE2進行重做。一次類推,小於checkpoint LSN的頁不用重做,大於LSN checkpoint的PAGE就要進行重做。
Block no的最高位是描述block是否flush磁盤的標識位.通過lsn可以blockno,具體的計算過程是lsn是多少個512的整數倍,也就是no = lsn / 512 + 1;為什麼要加1呢,因為所處no的塊算成clac_lsn一定會小於傳入的lsn.所以要+1。其實就是block的數組索引值。checksum是通過從塊頭開始到塊的末尾前4個字節為止,做了一次數字疊加,代碼如下:
sum = 1; sh = 0; for(i = 0; i < OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_TRL_SIZE, i ++){ sum = sum & 0x7FFFFFFF; sum += (((ulint)(*(block + i))) << sh) + (ulint)(*(block + i)); sh ++; if(sh > 24) sh = 0; }在日志恢復的時候,innodb會對加載的block進行checksum校驗,以免在恢復過程中數據產生錯誤。事務的日志寫入是基於塊的,如果事務的日志大小小於496字節,那麼會合其他的事務日志合並在一個塊中,如果事務日志的大小大於496字節,那麼會以496為長度進行分離存儲。例如:T1 = 700字節大小,T2 = 100字節大小存儲結構如下: