原文:
https://blogs.oracle.com/mysqlinnodb/entry/redo_logging_in_innodb
1.redo log
innodb 作为事务型的存储引擎,为了保证事务的安全性,在数据库恢复过程中丢失尽可能少的事务,innodb采用redo:重做日志来实现事务的重现。
下面是学习笔记加上一些自己的理解:
innodb 中以数据页(data page)为单位存储数据,使用space_id,space_no来标识唯一数据页,数据的读写操作都在内存中进行,之后刷新到磁盘。也就是说
每一次数据更改,innodb需要先将数据先缓存到内存中,再进行变更。
redo log 生成过程
1.事务的数据页的修改首先发生在内存中,并被标记为脏页
2.事务的redo 记录生成存放在mtr buffer中,并传递给全局的redo log buffer
3.全局的redo log buffer 写入redo log文件,并flush到磁盘
4.数据脏页flush到磁盘
这里有几个问题:
1.redo log中记录的是什么信息?
redo log 用于数据恢复,因此,它需要包含数据变更之前的状态和发生的变动,以在恢复中重建数据页
2.redo log和脏页如何关联,如何保证刷新的顺序?
关联关系由lsn来实现
redo的持久化发生在数据脏页持久化之前,也就是WAL理论:
write-ahead logging (WAL).
下文的内容会更清楚回答这2个问题
2.redo log file
默认情况下innodb在数据目录中创建2个redo log 文件
ib_logfile0 and
ib_logfile1。配置参数:
innodb_log_file_size 用于配置文件大小。
innodb_log_files_in_group 配置文件个数
重做日志的更新方式为轮询,组内的日志文件写满后从第一个文件开始重写。
文件内部以块为单位,每一个块为512B,每一个文件有一个文件头,占4个块空间
2.1重做日志文件头包含的信息
开始的4个bytes存放日志组信息
之后的8个bytes存放这个日志文件中数据的lsn
文件内第二个日志块的第一个checkpoint
文件内第四个日志块的第二个checkpoint
2.2 日志块
重做日志文件可看成连续的日志块,除日志文件头以外,其他的日志块结构:
1.日志块编号
2.块内字节数
3.第一个mtr事务数据的偏移量?
4.日志块的checkpoint
5.日志块的checksum
LSN
log sequence number,8字节无符号整形,重做日志中的偏移量标示,用于日志的定位。
LSN
|
Description
|
log_sys->lsn
|
The redo log record that will be generated next will make use of this lsn
|
log_sys->flushed_to_disk_lsn
|
The redo log file is flushed upto this LSN. All redo log records whose LSN is < flushed_to_disk_lsn is safely on the disk.
|
log_sys->write_lsn
|
There is a currently running write operation which will write upto this LSN.
|
log_sys->current_flush_lsn
|
There is a currently running write + flush operation which will write upto this LSN.
|
LSN 指向数据脏页,redo log 记录,redo log文件。当数据页发生变化时,redo log产生,每一条redo log 在被复制到内存日志缓存时分配相应的LSN,。每一个数据页也关联到相应的LSN,页的LSN存放在每页的页头。页的LSN提供了在该页刷新之前需要刷新的重做日志LSN。
Global Log System Object
/** Redo log buffer */
struct log_t
{
lsn_t lsn; /*!< log sequence number */
ulint buf_free; /*!< first free offset within the log
buffer */
byte* buf_ptr; /* unaligned log buffer */
byte* buf; /*!< log buffer */
ulint buf_size; /*!< log buffer size in bytes */
ulint max_buf_free; /*!< recommended maximum value of
buf_free, after which the buffer is
flushed */
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 */
lsn_t write_lsn; /*!< end lsn for the current running
write */
ulint write_end_offset;/*!< the data in buffer has
been written up to this offset
when the current write ends:
this field will then be copied
to buf_next_to_write */
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 */
};
log_sys->buf 指向log buffer内存地址,存储 mtr_commit()产生的redo log记录
log_sys->buf_size 指定log buffer的大小
log_sys->buf_free 当前buffer中记录的偏移量,下次写入的开始位置。刷新到磁盘的结束位置。也就是有效数据位置。
log_sys->buf_next_to_write redo log 刷新到磁盘日志文件的偏移量
log_sys->flushed_to_disk_lsn 已经刷新到磁盘的便宜量。小于或等于log_sys->write_lsn and log_sys->lsn
log_sys->lsn 当前的lsn值,mtr_commit()后更新。
log_sys->write_lsn 当前写入buffer的lsn 值
log_sys->current_flush_lsn 当前刷新磁盘文件的lsn
Global In-memory Redo Log Buffer
innodb_log_buffer_size 默认8MB
当一个事务产生了数据更新,相应的redo log将产生在log buffer中,事务提交或者buffer满时,这些buffer将会写入到磁盘日志文件中。
当redo log buffer 满时, mtr_commit()产生的日志记录没有足够的空间来存放。就会调用log_buffer_flush_to_disk() 方法将buffer中的内容刷新到磁盘日志文件,函数将log_sys->buf_next_to_write to log_sys->buf_free的内容刷新到日志文件。在lsn关系上,log_sys->flushed_to_disk_lsn to log_sys->lsn。也是lsn在整个事务持久化过程扮演标尺作用,有利于数据恢复时准确地定位恢复日志点。
The mini transaction (mtr)
用于产生redo log 记录,mtr产生日志后先存入mtr buffer
1.生成类型为mtr_t的最小事务结构体
2.调用方法 mtr_start(),初始化最小事务的缓存
3.调用mlog_write_ulint() 生成redo log 记录
4.调用 mtr_commit() 从最小事务缓存提交事务redolog到全局redolog缓存中,脏页列表也加入到缓存池的刷新列表中。
mtr_t::log 存放redo log 记录, mtr_t::memo 存放最小事务产生的脏页列表。
结构体定义:
/* Mini-transaction handle and buffer */
struct mtr_t
{
dyn_array_t memo; /*!< memo stack for locks etc. */
dyn_array_t log; /*!< mini-transaction log */
unsigned inside_ibuf:1;
/*!< TRUE if inside ibuf changes */
unsigned modifications:1;
/*!< TRUE if the mini-transaction
modified buffer pool pages */
unsigned made_dirty:1;
/*!< TRUE if mtr has made at least
one buffer pool page dirty */
ulint n_log_recs;
/* count of how many page initial log records
have been written to the mtr log */
ulint n_freed_pages;
/* number of pages that have been freed in
this mini-transaction */
ulint log_mode; /* specifies which operations should be
logged; default value MTR_LOG_ALL */
lsn_t start_lsn;/* start lsn of the possible log entry for
this mtr */
lsn_t end_lsn;/* end lsn of the possible log entry for
this mtr */
};
Redo Log Record Types
physical redo logs 物理重做日志,将记录数据变动,日志文件和数据文件同样大小
logical redo logs 逻辑重做日志,记录重组数据页的必要信息,日志内容小
Life cycle of a redo log record 重做日志的生命周期
1.最小事务(mtr)产生重做日志记录并保存在最小事务缓存中,记录包含事务重做需要的信息
2.mtr_commit()完成将重做日志记录传输到全局重做日志缓存中,如果缓存空间不足,全局重做日志缓存将刷新到磁盘
3.重做日志记录包含一个特殊的关联lsn,这个lsn在重做日志记录从最小事务缓存传输到全局重做事务缓存的过程中确定。重做日志记录的lsn一旦确定,它在磁盘日志文件的位置也确定下来
4.重做日志记录从全局重做日志缓存中刷新到重做日志文件中。这意味这重做日志记录被持久化
5.每一条重做日志记录包含一个关联的脏页列表,这个关系由lsn来确定,重做日志记录必须先于他关联的脏页刷新到磁盘中,在重做日志文件中一条重做日志记录必须在其关联的脏页刷新到磁盘后才能被覆盖
6.在系统恢复过程中,重做日志用于重建他关联列表包含的脏页