# 一、redo log block
设计InnoDB时为了更好的进行系统奔溃恢复,将通过mtr生成的redo日志放在大小为512字节的页中。为了和表空间中的页做区别,于是把用来存储redo日志的页称为block。一个redo log block的示意图如下:

真正的redo日志都是存储到占用496字节大小的log block body中。另外,log block header占12个字节,log block trailer占4个字节,存储的是一些管理信息。
其中,log block header中包含以下属性字段:
LOG_BLOCK_HDR_NO(4B):每一个block都有一个大于0的唯一标号,该属性就表示该标号值。LOG_BLOCK_HDR_DATA_LEN(2B):表示block中已经使用了多少字节,初始值为12。随着往block中写入的redo日志越来也多,该值也跟着增长。如果log block body已经被全部写满,那么值被设置为512。LOG_BLOCK_FIRST_REC_GROUP(2B):一条redo日志也可以称之为一条redo日志记录,一个mtr会生产多条redo日志记录,这些redo日志记录被称之为一个redo日志记录组(redo log record group)。LOG_BLOCK_FIRST_REC_GROUP就代表该block中第一个mtr生成的redo日志记录组的偏移量,即这个block里第一个mtr生成的第一条redo日志的偏移量。LOG_BLOCK_CHECKPOINT_NO(4B):表示checkpoint的序号。
log block trailer中包含的属性字段为:
LOG_BLOCK_CHECKSUM(4B):表示block的校验值,用于正确性校验。
二、redo日志缓冲区
与为了解决磁盘速度过慢的问题而引入了Buffer Pool的思想类似,写入redo日志时也不能直接直接写到磁盘上。实际上在服务器启动时就向操作系统申请了一大⽚称之为redo log buffer的连续内存空间,即redo日志缓冲区,也可以简称为log buffer。这⽚内存空间被划分成若干个连续的redo log block,如图所示:

三、redo日志写入log buffer
向log buffer中写入redo日志的过程是顺序的,也就是先往前边的block中写,当该block的空闲空间用完之后再往下一个block中写。因此,当往log buffer中写入redo日志时,第一个遇到的问题就是应该写在哪个block的哪个偏移量处,所以InnoDB的特意提供了一个称之为buf_free的全局变量,该变量指明后续写入的redo日志应该写入到log buffer中的哪个位置。

由于一个mtr执行过程中可能产生若干条redo日志,这些redo日志是一个不可分割的组,所以其实并不是每生成一条redo日志,就将其插入到log buffer中,而是每个mtr运行过程中产生的日志先暂时存到一个地方,当该mtr结束的时候,将过程中产生的一组redo日志再全部复制到log buffer中。
四、源码解析
4.1 log buffer结构体
/** Redo log buffer */
struct log_t{
char pad1[CACHE_LINE_SIZE];
/*!< Padding to prevent other memory
update hotspots from residing on the
same memory cache line */
lsn_t lsn; /*!< log sequence number */
ulint buf_free; /*!< first free offset within the log
buffer in use */
byte* buf_ptr; /*!< unaligned log buffer, which should
be of double of buf_size */
byte* buf; /*!< log buffer currently in use;
this could point to either the first
half of the aligned(buf_ptr) or the
second half in turns, so that log
write/flush to disk don't block
concurrent mtrs which will write
log to this buffer */
bool first_in_use; /*!< true if buf points to the first
half of the aligned(buf_ptr), false
if the second half */
ulint buf_size; /*!< log buffer size of each in bytes */
ulint max_buf_free; /*!< recommended maximum value of
buf_free for the buffer in use, after
which the buffer is flushed */
bool check_flush_or_checkpoint;
/*!< this is set when there may
be need to flush the log buffer, or
preflush buffer pool pages, or make
a checkpoint; this MUST be TRUE when
lsn - last_checkpoint_lsn >
max_checkpoint_age; this flag is
peeked at by log_free_check(), which
does not reserve the log mutex */
UT_LIST_BASE_NODE_T(log_group_t)
log_groups; /*!< log groups */
#ifndef UNIV_HOTBACKUP
/** The fields involved in the log buffer flush @{ */
ulint buf_next_to_write;/*!< first offset in the log buffer
where the byte content may not exist
written to file, e.g., the start
offset of a log record catenated
later; this is advanced when a flush
operation is completed to all the log
groups */
volatile bool is_extending; /*!< this is set to true during extend
the log buffer size */
lsn_t write_lsn; /*!< last written lsn */
lsn_t current_flush_lsn;/*!< end lsn for the current running
write + flush operation */
lsn_t flushed_to_disk_lsn;
/*!< how far we have written the log
AND flushed to disk */
ulint n_pending_flushes;/*!< number of currently
pending flushes; incrementing is
protected by the log mutex;
may be decremented between
resetting and setting flush_event */
os_event_t flush_event; /*!< this event is in the reset state
when a flush is running; a thread
should wait for this without
owning the log mutex, but NOTE that
to set this event, the
thread MUST own the log mutex! */
ulint n_log_ios; /*!< number of log i/os initiated thus
far */
ulint n_log_ios_old; /*!< number of log i/o's at the
previous printout */
time_t last_printout_time;/*!< when log_print was last time
called */
/* @} */
/** Fields involved in checkpoints @{ */
lsn_t log_group_capacity; /*!< capacity of the log group; if
the checkpoint age exceeds this, it is
a serious error because it is possible
we will then overwrite log and spoil
crash recovery */
lsn_t max_modified_age_async;
/*!< when this recommended
value for lsn -
buf_pool_get_oldest_modification()
is exceeded, we start an
asynchronous preflush of pool pages */
lsn_t max_modified_age_sync;
/*!< when this recommended
value for lsn -
buf_pool_get_oldest_modification()
is exceeded, we start a
synchronous preflush of pool pages */
lsn_t max_checkpoint_age_async;
/*!< when this checkpoint age
is exceeded we start an
asynchronous writing of a new
checkpoint */
lsn_t max_checkpoint_age;
/*!< this is the maximum allowed value
for lsn - last_checkpoint_lsn when a
new query step is started */
ib_uint64_t next_checkpoint_no;
/*!< next checkpoint number */
lsn_t last_checkpoint_lsn;
/*!< latest checkpoint lsn */

最低0.47元/天 解锁文章
714

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



