版本
mysql-8.0.22 社区版
源码
结合源码来介绍redo日志的各个模块。
redo日志的写入
写入系统磁盘
redo日志刷盘是将所有的redo文件进行fsync,确保系统缓存中的redo日志内容存储到磁盘上。
redo刷盘由用户线程执行、或者系统后台线程执行。刷盘接口为统一的接口、
相关变量定义
log_t *log_sys
| flushed_to_disk_lsn //当前磁盘刷新到的位置
刷新磁盘
刷新磁盘的源码如下:
log_flush_low
| const lsn_t last_flush_lsn = log.flushed_to_disk_lsn.load();
| const lsn_t flush_up_to_lsn = log.write_lsn.load();
// 如果flush_lsn 小于 write_lsn 就进行刷盘
| if (last_flush_lsn < flush_up_to_lsn)
| fil_flush_file_redo()
| | // 对所有的redo文件进行刷盘
| | for (auto &file : space->files)
| | os_file_flush(file.handle);
redo的checkpoint
redo日志的checkpoint机制是为了保证redo日志的复用。在WAL机制下,redo日志优先落盘,数据页后续异步后台线程落盘。当数据页落盘之后,redo日志便可以回收复用。
相关变量定义
log_t *log_sys
| last_checkpoint_lsn //当前磁盘checkpoint到的位置
| checkpoint_buf // 用来更新redo的header的buffer
| recent_closed //判断当前可以刷脏的最大LSN
checkpoint
判断可以checkpoint的最大LSN
log_update_available_for_checkpoint_lsn
1. 当前可以刷脏的最大LSN,
| log_buffer_dirty_pages_added_up_to_lsn(log);
| recent_closed.tail()
2. flush list中最小的lsn向后退一个recent_closed.capacity()
| lsn_t lwm_lsn = buf_pool_get_oldest_modification_lwm();
3. flush_lsn 和 checkpoint_lsn取小
| log.flushed_to_disk_lsn.load() // log.last_checkpoint_lsn.load()
4. 取三项中最小的LSN最为本次可以 checkpoint的LSN
| log.available_for_checkpoint_lsn = oldest_lsn;
recent_closed是在将数据页加入flush_list时使用,原理同write_recnet类似,用以保证recent_closed.tail()之前的数据页都已经加入到flush_list中,在刷脏时不会有遗漏的情况。
checkpoint
log_consider_checkpoint(log);
// 先将dynamic_metadata
| dict_persist_to_dd_table_buffer();
| log_checkpoint(log);
| | log_files_write_checkpoint(log, checkpoint_lsn);
| | // 生成一个checkpoint_no
| | checkpoint_no = log.next_checkpoint_no.load();
// 将checkpoint相关信息写入buffer
| | mach_write_to_8(buf + LOG_CHECKPOINT_NO, checkpoint_no);
| | mach_write_to_8(buf + LOG_CHECKPOINT_LSN, next_checkpoint_lsn);
// 写入系统缓存并刷盘
| | err = fil_redo_io( IORequestLogWrite,
| | log_fsync();
redo使用两个BLOCK来记录checkpoint相关信息,根据checkpoint_no来决定使用哪一个,分别记录在redo日志第2个和第4个BLOCK中,两个checkpoint的block交替使用。