WiredTiger的journal日誌實現

journal日誌是實現WiredTiger持久化的關鍵, 所有的插入、更新操作, 都會在journal裏面寫入一條日誌, 並且在服務器意外退出的時候, 通過checkpoint和該checkpoint之後的日誌, 能夠迅速的回覆服務器的狀態。
在Wiredtiger裏面, 通過log來表示journal相關的實現, 接下來, 我們看下是如何實現的。

log相關的數據結構

在WT_CONNECTION_IMPL中定義瞭如下的字段, 它們主要記錄瞭如下的主要內容:

  • log server的線程;
    該線程是journal打開後, 沒收到一個日誌, 進行一次將日誌寫入磁盤;

  • log file close線程;
    該線程用來關閉的文件log->log_close_fh,並且更新log->sync_lsn到關閉文件的最大的LSN; 發送信號log->log_sync_cond;

  • log wrlsn線程;
    用來處理written slot, 儘可能的合併slot, 並且更新log->write_lsn, log->write_start_lsn, 並且發送log->log_write_cond信號, 如果設定了文件關閉, 還會發送conn->log_file_cond信號, 給log_file_close線程;

  • WT_CONNECTION_IMPL內關於log先關的其他記錄;

這3個線程, 都是在conn_log.c文件的__wt_logmgr_open函數裏面來實現的。

struct __wt_connection_impl {
...
#define WT_CONN_LOG_ARCHIVE  0x01 /* Archive is enabled */
#define WT_CONN_LOG_ENABLED  0x02 /* Logging is enabled */
#define WT_CONN_LOG_EXISTED  0x04 /* Log files found */
#define WT_CONN_LOG_RECOVER_DONE 0x08 /* Recovery completed */
#define WT_CONN_LOG_RECOVER_ERR  0x10 /* Error if recovery required */
#define WT_CONN_LOG_ZERO_FILL  0x20 /* Manually zero files */
 uint32_t  log_flags; /* Global logging configuration */
 
// log 主線程
 WT_CONDVAR *log_cond; /* Log server wait mutex */
 WT_SESSION_IMPL *log_session; /* Log server session */
 wt_thread_t  log_tid; /* Log server thread */
 bool   log_tid_set; /* Log server thread set */

// log file線程相關
 WT_CONDVAR *log_file_cond; /* Log file thread wait mutex */
 WT_SESSION_IMPL *log_file_session;/* Log file thread session */
 wt_thread_t  log_file_tid; /* Log file thread */
 bool   log_file_tid_set;/* Log file thread set */

// log 寫入LSN線程相關
 WT_CONDVAR *log_wrlsn_cond;/* Log write lsn thread wait mutex */
 WT_SESSION_IMPL *log_wrlsn_session;/* Log write lsn thread session */
 wt_thread_t  log_wrlsn_tid; /* Log write lsn thread */
 bool   log_wrlsn_tid_set;/* Log write lsn thread set */

// 全局log先關的字段記錄
 WT_LOG  *log;  /* Logging structure */
 WT_COMPRESSOR *log_compressor;/* Logging compressor */
 wt_off_t  log_file_max; /* Log file max size */
 const char *log_path; /* Logging path format */
 uint32_t  log_prealloc; /* Log file pre-allocation */
 uint32_t  txn_logsync; /* Log sync configuration */
}

在每個connection裏面, 有一個全局的log對象, 它記錄了所有的journal狀態信息, 以及一些統計信息。
WT_LOG主要記錄瞭如下的信息:

  • log的底層文件ID以及句柄;
  • log底層的buffer相關記錄;
  • journal的LSN;
  • journal線程間同步的spinLock 以及Condition Variable;

在connection創建的時候, 會創建一組WT_SLOT_POOL=128個256K的slot數組, 在某個時候會有一個座位active_slot, 新的journal會寫入到該slot, 直到slot寫滿轉入到下一個新的slot。 WT_LOGSLOT主要是記錄slot狀態, 以及slot_pool的相關信息。
WT_LOGSLOT主要記錄如下信息:

  • slot的狀態信息;
  • slot對應的buffer;
  • slot對應的底層文件;
  • 該slot在日誌文件的位置信息以及LSN信息;

WT_MYSLOT

WT_MYSLOT主要記錄如下信息:

  • 當前使用的slot;
  • slot在buffer裏面的其實位置;

在接口層面有2個journal相關的接口:

WT_SESSION->log_printf; // __session_log_printf, 寫入一條日誌;
WT_SESSION->log_flush; // __session_log_flush, 將緩存的日誌刷到磁盤;

log 寫入過程

對每一條日誌, 產生一個WT_LOG_RECORD對象, 通過__wt_log_write調用__log_write_internal將該對象寫入。

  • 先經過函數__wt_log_slot_join, 將新的日誌根據日誌的大小以及前一個日誌結束位置計算出要寫入的位置更新;
  • 判斷是否強制將log內存寫入磁盤, 如果是由__wt_log_slot_switch, 將緩存內容寫入磁盤;
  • 否則, 通過__log_fill, 將日誌copy到緩存buffer裏面;
  • 如果slot已經關閉, 就將日誌寫入磁盤;
  • 否則,如果強制同步, 若等待slot寫入, 發送conn->log_cond信號; 否則,強制轉換新的slot, release當前的slot;
  • 如果指定了WT_LOG_FLUSH, 等待log_write_cond信號;
  • 如果指定了WT_LOG_FSYNC, 等待log_sync_cond信號;
  • 如果指定了WT_LOG_BACKGROUND, 更新log->bg_sync_lsn, 發送信號log_file_cond;

刷新log的過程

調用__wt_log_force_write, 將active_slot close, 並且switch到新的active_slot,然後通過__wt_log_wrlsn, 更新log裏面寫入的位置以及LSN, 並且必要時發送log->log_write_cond信號;
如果設定了WT_SLOT_CLOSEFH, 發送conn->log_file_cond信號;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章