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 */
}
WT_LOG

在每个connection里面, 有一个全局的log对象, 它记录了所有的journal状态信息, 以及一些统计信息。
WT_LOG主要记录了如下的信息:

  • log的底层文件ID以及句柄;
  • log底层的buffer相关记录;
  • journal的LSN;
  • journal线程间同步的spinLock 以及Condition Variable;
WT_LOGSLOT

在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信号;
flush的过程, 就是把当前的active_slot关闭, 将内容写入文件, 更新lsn的状态, 如下代码:

static int
__log_slot_switch_internal(
    WT_SESSION_IMPL *session, WT_MYSLOT *myslot, bool forced)
{


	if (!F_ISSET(myslot, WT_MYSLOT_CLOSE)) {
	   // 关闭正在使用的slot, 用WT_MYSLOT来记录, 将其状态变成WT_LOG_SLOT_CLOSE, 
	   // 并且根据slot里面数据大小更新myslot->slot_end_lsn, 用这个值更新conn->log->all_lsn代表新的lsn的值 
		ret = __log_slot_close(session, slot, &release, forced);

		if (release) {
		   	// log release 主要做如下事情:
		   	// 根据log_written判断当前是否要触发一次checkpoint;
		   	// 写入slot buffer内容到log文件;
		   	// 如实不需要同步, 结束;
		   // 等待之前的写入完成, 这里会向log wrlsn线程发送信号, 并且等待写入线程;
		   	//  更新conn->log write_start_lsn & write_lsn,然后发送信号给log write线程写入;
		   	// 如果需要调用fsync, 更新上一次sync的lsn:log->sync_lsn, 并发送信号给等待sync_lsn变化的线程;
			WT_RET(__wt_log_release(session, slot, &free_slot));
			if (free_slot)
			 // 将slot状态设定为初始值, 给后面复用
				__wt_log_slot_free(session, slot);
		}
	}


    // 获取一个新的slot作为active_slot
	WT_RET(__wt_log_slot_new(session));
	
	return (0);
}

整体的流程图如下:
在这里插入图片描述

事务的日志

事务transaction的日志和普通的增删改操作的日志是很类似, 但是也有不同的地方。
相同的地方:都是通过__wt_txn_log_op函数, 将所有的操作都放进到__txn_logrec_init产生的WT_LOGRECORD对象的buffer日志里面。
不同的地方是: 这里的每个__wt_log_record对象可能包含很多的操作, 等到transaction commit的时候, 在一起讲所有的日志写入到文件。

日志的提交与同步

日志的提交方式有:
写入内存(memcpy), 写入系统(sync=off, write), 刷新到磁盘(sync=on, fsync);
日志的同步的方式:
background同步(sync=background), 前置写盘同步(sync=fsync), 不需要强制同步(sync=off)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值