# 1、日志文件组
MySQL的数据目录下默认有两个名为ib_logfile0和ib_logfile1的文件,log buffer中的日志默认情况下就是刷新到这两个磁盘文件中。
如果对默认的redo日志文件不满意,可以通过下边几个启动参数来调节:
innodb_log_group_home_dir:该参数指定了redo日志文件所在的目录,默认值就是当前的数据目录。innodb_log_file_size:该参数指定了每个redo日志文件的大小,默认值为48MBinnodb_log_files_in_group:该参数指定redo日志文件的个数,默认值为2,最大值为100。
从上边的描述中可以看到,磁盘上的redo日志文件不只一个,而是以一个日志文件组的形式出现的。这些文件以ib_logfile[数字](数字可以是0、1、2…)的形式进行命名。在将redo日志写入日志文件组时,是从ib_logfile0开始写,如果ib_logfile0写满了,就接着ib_logfile1写,同理,ib_logfile1写满了就去写ib_logfile2,依此类推。如果写到最后一个文件该咋办?那就重新转到ib_logfile0继续写,所以整个过程如下图所示:

总共的redo日志文件大小其实就是:innodb_log_file_size × innodb_log_files_in_group
2、redo日志文件格式
前文说过log buffer本质上是一片连续的内存空间,被划分成了若干个512字节大小的block。将log buffer中的redo日志刷新到磁盘的本质就是把block的镜像写入日志文件中,所以redo日志文件其实也是由若干个512字节大小的block组成。redo日志文件组中的每个文件大小都一样,格式也一样,都是由两部分组成:
- 前2048个字节,也就是前4个block是用来存储一些管理信息的
- 从第2048字节往后是用来存储
log buffer中的block镜像的
所以上文所说的循环使用redo日志文件,其实是从每个日志文件的第2048个字节开始算,如图所示:

这里需要介绍一下每个redo日志文件前2048个字节,也就是前4个特殊block的格式是怎样的,如图所示。

其中,第一个block为log file header,包含以下属性:
LOG_HEADER_FORMAT: 4B,redo日志的版本,该值永远为1LOG_HEADER_PAD1: 4B,字节填充LOG_HEADER_START_LSN: 8B,标记本redo日志文件开始的LSN值,也就是文件偏移量为2048字节初对应的LSN值LOG_HEADER_CREATOR: 32B,一个字符串,标记本redo日志文件的创建者是谁。正常运行时该值为MySQL的版本号,比如:"MySQL使用mysqlbackup命令创建的redo日志文件的该值为"ibbackup"和创建时间。LOG_BLOCK_CHECKSUM: 4B,本block的校验值

第二个block为checkpoint1,该块中记录了关于checkpoint一些属性:
LOG_CHECKPOINT_NO: 8B,服务器做的checkpoint编号,每做一次checkpoint,该值就加1。LOG_CHECKPOINT_LSN: 8B,服务器做checkpoint结束时对应的LSN值,系统奔溃恢复时将从该值开始。LOG_CHECKPOINT_OFFSET: 8B,上个属性中的LSN值在redo日志文件组中的偏移量LOG_CHECKPOINT_LOG_BUF_SIZE: 8B,服务器在做checkpoint操作时对应的logbuffer的大小LOG_BLOCK_CHECKSUM: 4B,本block的校验值

第四个blockcheckpoint2与checkpoint1中的结构属性相同,因此不做赘述。
3、源码解析
3.1 日志文件组结构体
struct log_group_t{
/** log group identifier (always 0) */
ulint id;
/** number of files in the group */
ulint n_files;
/** format of the redo log: e.g., LOG_HEADER_FORMAT_CURRENT */
ulint format;
/** individual log file size in bytes, including the header */
lsn_t file_size
/** file space which implements the log group */;
ulint space_id;
/** corruption status */
log_group_state_t state;
/** lsn used to fix coordinates within the log group */
lsn_t lsn;
/** the byte offset of the above lsn */
lsn_t lsn_offset;
/** unaligned buffers */
byte** file_header_bufs_ptr;
/** buffers for each file header in the group */
byte** file_header_bufs;
/** used only in recovery: recovery scan succeeded up to this
lsn in this log group */
lsn_t scanned_lsn;
/** unaligned checkpoint header */
byte* checkpoint_buf_ptr;
/** buffer for writing a checkpoint header */
byte* checkpoint_buf;
/** list of log groups */
UT_LIST_NODE_T(log_group_t) log_groups;
};
日志文件组结构体中包含了文件组id、文件数量、所包含redo日志文件的类型等基本信息。另外,结构体中还有几个较为重要的字段:
lsn: 日志组中用于确定位置的lsnlsn_offset: lsn的字节偏移量checkpoint_buf: 用于写入检查点头部的缓冲区UT_LIST_NODE_T(log_group_t) log_groups: 将日志组链接起来的链表
3.2 刷盘时机
理想状态下,事务一提交就会进行刷盘操作,但实际上,刷盘的时机是根据策略来进行的。
InnoDB存储引擎为redo log的刷盘策略提供了innodb_flush_log_at_trx_commit参数,它支持三种策略:
- 0 :设置为 0 的时候,表示每次事务提交时不进行刷盘操作
- 1 :设置为 1 的时候,表示每次事务提交时都将进行刷盘操作(默认值)
- 2 :设置为 2 的时候,表示每次事务提交时都只把
redo log buffer内容写入page cache innodb_flush_log_at_trx_commit参数默认为 1 ,也就是说当事务提交时会调用fsync对redo log进行刷盘- 另外,InnoDB 存储引擎有一个后台线程,每隔1 秒,就会把
redo log buffer中的内容写到文件系统缓存(page cache),然后调用fsync刷盘。
3.2.1 提交刷盘
提交刷盘主要涉及到两个关键函数:log_buffer_flush_to_disk()和log_write_up_to()
void
log_buffer_flush_to_disk(
bool sync)
{
ut_ad(!srv_read_only_mode);
log_write_up_to(log_get_lsn(), sync);
}
- 这段代码的功能是调用
log_write_up_to()函数将日志缓冲区中的内容写入到磁盘上的日志文件中,直到最新的日志记录,并根据参数决定是否同时将日志数据刷新到磁盘。 - 是否进行刷盘操作主要通过
sync参数控制:sync为true,则进行刷盘操作;否则只写入操作系统的缓存。默认情况下,bool sync = true。
void
log_write_up_to(
lsn_t lsn,
bool flush_to_disk)
{
byte* write_buf;
lsn_t write_lsn;
ut_ad(!srv_read_only_mode);
if (recv_no_ibuf_operations) {
return;
}
loop:
ut_ad(++loop_count < 128);
log_write_mutex_enter();
ut_ad(!recv_no_log_write);
lsn_t limit_lsn = flush_to_disk
? log_sys->flush

最低0.47元/天 解锁文章
714

被折叠的 条评论
为什么被折叠?



