mysql 8.0 源码笔记:REDO日志(2)

版本

mysql-8.0.22 社区版

源码

结合源码来介绍redo日志的各个模块。

redo日志的写入

写入系统缓存

log buffer是循环使用,需要将log buffer中的内容,持续的写入到系统缓存中。有较多的地方会将log buffer中的内容写入系统缓存,比如commit、比如后台线程持续的写入等等。

相关变量定义
log_t *log_sys
| write_lsn 					// 当前写入系统缓存的最大的LSN

| current_file_lsn  			// 通过LSN计算对应物理文件的位置
| current_file_real_offset
| current_file_end_offset		// 当前文件的最大OFFSET

| recent_written     			// 用户判断当前可以写入的最大LSN
写入

判断可以写入的最大LSN
在将log buffer写入到系统缓存时,我们需要写入连续的内存,不能留有空洞,这样才能保证write_lsn之前的log buffer是可以复用的。redo日志是并发写入的,这里引入recent_written来保证连续的redo日志写入。
recent_written的定义如下:

Link_buf<lsn_t> recent_written
| m_capacity; 					// 该Link_buf最大的lsn长度
| m_links						
| m_tail						// 当前可以write的最大lsn

流程如下:

  1. 每次写入redo日志到log buffer时,会将该日志的start_lsn 和 end_lsn记录到recent_written中,使用m_links[start_lsn] = end_lsn来记录。LSN是连续的,那么end_lsn为下一次redo日志的起始位置。
  2. 当查找时,如果m_links[curr_lsn]不为0,则表示这一段lsn还没有写入到log buffer中。当前允许写入到系统缓存的最大lsn就是curr_lsn。

源码如下:

Link_buf<Position>::add_link (Position from, Position to)
| index = from & (m_capacity - 1)
| m_links[index].store(to)


Link_buf<Position>::advance_tail
| from = m_tail.load()
| next = m_links[from]
  // mlinks 没有被更新 (0 或者 遗留的旧值) 则表示还没有写入
  // 如果有值则继续推进,一直到第一个没有写入
| if (next < from) return
  else m_tail.store(from )

举例说明:
三条redo日志的lsn为(10,15)、(15,20)、(20,25)。当第一条和第三条写入完成时,recent_written中的记录为:

recent_written.m_tail = 10

m_links[10] = 15
m_links[15] = 0
m_links[20] = 25

// 调用该函数后,m_tail更新为15,表示LSN为15之前的log buffer可以写入系统缓存
Link_buf<Position>::advance_tail()

后台线程 log_writter 写入流程

后台线程,log buffer写入系统缓存的流程如下:

  1. 获取本次写入能写到的最大LSN
  2. 通过输入的LSN计算本次写入的起始位置和结束位置
  3. 自适应调整结束位置:不跨文件边界,使用的redo日志空间可以被覆盖
  4. 将LSN转换为redo日志文件的位置,并进行插入
  5. 更新write_lsn等元数据

log buffer写入系统缓存的接口如下

 1. 通过recent_written获得当能能写入的最大的LSN
ready_lsn = log_buffer_ready_for_write_lsn(log);
| return log.recent_written.tail()


log_writer_write_buffer(log, ready_lsn)
| 2. 计算起始和结束位置start_offset、 end_offset
| size_t start_offset = write_lsn % log.buf_size;
| size_t end_offset = ready_lsn % log.buf_size;

| 3. 自适应的调整写入的最大LSN
| if (start_offset >= end_offset)  end_offset = log.buf_size;
| if (checkpoint_limited_lsn < ready_lsn)  ready_lsn = checkpoint_limited_lsn;

| 4. 将lsn转换为物理文件的位置并进行插入
| log_files_write_buffer( log, buf_begin, buf_end - buf_begin, ...
| | real_offset = compute_real_offset(log, start_lsn)
| | ...
| | write_blocks(log, write_buf, write_size, real_offset);
| |
| | 5. 更新write_lsn、buf_limit_sn(log buffer写入上限)等原数据
| | log.write_lsn.store(new_write_lsn);
| | log_update_buf_limit(log, new_write_lsn);

commit 写入流程
这里包括autocommit、ddl 等操作,不局限于某一种操作。写入流程如下:

  1. 等待可写入lsn 超过输入的参数 lsn
  2. 循环写入,直到write_lsn 到达或者超过 lsn

写入接口如下:

// 参数flush决定log buffer的redo写入系统缓存后,是否刷新内容到磁盘,这里暂时不做讨论。

log_write_up_to(*log_sys, lsn, flush) <==> log_self_write_up_to
  1. 等待ready_lsn 超过输入的lsn
| while (i < srv_n_spin_wait_rounds && ready_lsn < end_lsn) {
|  log.recent_written.advance_tail();
   ready_lsn = log_buffer_ready_for_write_lsn(log);

  2. 等待写入的lsn到ready_lsn
| while (write_lsn < ready_lsn) 
|  // 这里的写入同后台线程相同
|  log_writer_write_buffer(log, log_buffer_ready_for_write_lsn(log));


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值