MySQL系列:innodb源码分析之重做日志结构(三)

2015-01-23 22:04:52 · 作者: · 浏览: 10
int_lsn; ulint n_pending_checkpoint_writes; rw_lock_t checkpoint_lock; /*checkpoint的rw_lock_t,在checkpoint的时候,是独占这个latch*/ byte* checkpoint_buf; /*checkpoint信息存储的buf*/ ulint archiving_state; dulint archived_lsn; dulint max_archived_lsn_age_async; dulint max_archived_lsn_age; dulint next_archived_lsn; ulint archiving_phase; ulint n_pending_archive_ios; rw_lock_t archive_lock; ulint archive_buf_size; byte* archive_buf; os_event_t archiving_on; ibool online_backup_state; /*是否在backup*/ dulint online_backup_lsn; /*backup时的lsn*/ UT_LIST_BASE_NODE_T(log_group_t) log_groups; }log_t;

3.3.1各种LSN之间的关系和分析

从上面的结构定义可以看出有很多LSN相关的定义,那么这些LSN直接的关系是怎么样的呢?理解这些LSN之间的关系对理解整个重做日志系统的运作机理会有极大的信心。以下各种LSN的解释:

lsn 当前log系统最后写入日志时的LSN

flush_lsn redolog buffer最后一次数据刷盘数据末尾的LSN,作为下次刷盘的起始LSN

written_to_some_lsn 单个日志组最后一次日志刷盘时的起始LSN

written_to_all_lsn 所有日志组最后一次日志刷盘是的起始LSN

last_checkpoint_lsn 最后一次建立checkpoint日志数据起始的LSN

next_checkpoint_lsn 下一次建立checkpoint的日志 数据起始的LSN,用log_buf_pool_get_oldest_modification获得的

archived_lsn 最后一次归档日志数据起始的LSN

next_archived_lsn 下一次归档日志数据的其实LSN

关系图如下:

\

3.3.2偏移量的分析

?

log_t有各种偏移量,例如:max_buf_free、buf_free、flush_end_offset、buf_next_to_write等。偏移和LSN不一样,偏移量是相对redo log buf其实位置的绝对偏移量,LSN是整个日志系统的序号。

max_buf_free 写入日志是不能超过的偏移位置,如果超过,将强制redo log buf写入磁盘

buf_free 当前日志可以写的偏移位置

buf_next_to_write 下一次redo log buf数据写盘的数据起始偏移,在所有刷盘IO完成后,其值和 flush_end_offset是一致的。

flush_end_offset 本次刷盘的数据末尾的偏移量,相当于刷盘时的buf_free,当flush_end_offset 超过max_buf_free的一半时会将未写入的数据移到 redobuffer的最前面,这时buf_free和buf_next_to_write都将做调整

大小关系图如下:

\

?

3.4内存结构关系图

\

?

?

4.日志写入和日志保护机制

?

innodb有四种日志刷盘行为,分别是异步redo log buffer刷盘、同步redo log buffer刷盘、异步建立checkpoint刷盘和同步建立checkpoint刷盘。在innodb中,刷盘行为是非常耗磁盘IO的,innodb对刷盘做了一套非常完善的策略。

?

4.1重做日志刷盘选项

在innodb引擎中有个全局变量srv_flush_log_at_trx_commit,这个全局变量是控制flushdisk的策略,也就是确定调不调用fsync这个函数,什么时候掉这个函数。这个变量有3个值。这三个值的解释如下:

0 每隔1秒由MasterThread控制重做日志模块调用log_flush_to_disk来刷盘,好处是提高了效率,坏处是1秒内如果数据库崩溃,日志和数据会丢失。

1 每次写入重做日志后,都调用fsync来进行日志写入磁盘。好处是每次日志都写入了磁盘,数据可靠性大大提高,坏处是每次调用fsync会产生大量的磁盘IO,影响数据库性能。

2 每次写入重做日志后,都将日志写入日志文件的page cache。这种情况如果物理机崩溃后,所有的日志都将丢失。

4.2日志刷盘保护

由于重做日志是一个组内多文件重复写的一个过程,那么意味日志如果不及时写盘和创建checkpoint,就有可能会产生日志覆盖,这是一个我们不愿意看到的。在innodb定义了一个日志保护机制,在存储引擎会定时调用log_check_margins日志函数来检查保护机制。简单介绍如下:

引入三个变量 buf_age、checkpoint_age和日志空间大小.

buf_age = lsn -oldest_lsn;

checkpoint_age =lsn - last_checkpoint_lsn;

日志空间大小 = 重做日志组能存储日志的字节数(通过log_group_get_capacity获得);

当buf_age >=日志空间大小的7/8时,重做日志系统会将red log buffer进行异步数据刷盘,这个时候因为是异步的,不会造成数据操作阻塞。

?

当buf_age >=日志空间大小的15/16时,重做日志系统会将redlog buffer进行同步数据刷盘,这个时候会调用fsync函数,数据库的操作会进行阻塞。

?

当 checkpoint_age >=日志空间大小的31/32时,日志系统将进行异步创建checkpoint,数据库的操作不会阻塞。

?

当 checkpoint_age == 日志空间大小时,日志系统将进行同步创建checkpoint,大量的表空间脏页和log文件脏页同步刷入磁盘,会产生大量的磁盘IO操作。数据库操作会堵塞。整个数据库事务会挂起。

5.总结

Innodb的重做日志系统是相当完备的,它为数据的持久化做了很多细微的考虑,它效率直接影响MySQL的写效率,所以我们深入理解了它便以我们去优化它,尤其是在大量数据刷盘的时候。假设数据库的受理的事务速度大于磁盘IO的刷入速度,一定会出现同步建立checkpoint操作,这样数据库是堵塞的,整个数据库都在都在进行脏页刷盘。避免这样的问题发生就是增加IO能力,用多磁盘分散IO压力。也可以考虑SSD这读写速度很高的存储介质来做优化。

?

?