type
Post
status
Published
date
Mar 9, 2023
slug
summary
tags
数据库
InnoDB
category
学习思考
password
icon
mini-transaction 是内部更低级的概念,简称mtr,修改Page、新增Page等都需要写入Rodo log,用于Crash之后恢复。在全局有一个
log_sys 对象,作为redo log元配置对应的结构是log0sys.h的log_t 。log_sys 作为redo log的物理和逻辑(redo buffer)枢纽。LSN(Log Sequeue Number):日志序号,初始值
8704 按照实际写入的日志量加上占用的log block header和log block trailer来计算。SN(Sequeue Number):
log buffer 写入位置。LSN和SN之间的换算关系:
日志结构

事务流程
mini-transaction start
mini-transaction 结构
mtr.start() 方法初始化会把 m_log 初始化 new (&m_impl.m_memo) mtr_buf_t();mini-transaction 数据插入
操作之前会获取各种锁。
mlog_open
首先标记 buf 数据已被修改
mtr m_impl.m_modifications = true;获取
mtr_t.Impl.m_log 的指针。有可能会扩容。mlog_write_initial_log_record_fast
向
m_log中写入type,space id,page no,并增加m_n_log_recs的数量。mach_write_compressed
根据数字的具体大小,选择从1到4个字节记录整数,写入
m_log 。mlog_close
mini-transaction commit
commit 实际上是间接调用
Command.execute 方法,首先执行prepare_write(); 做写日志的准备,如果只生成一条redo log 会把Flag 设置 MLOG_SINGLE_REC_FLAG ,如果是多条会在尾部添加 MLOG_MULTI_REC_END 标志。MLOG_MULTI_REC_END 用于多条日志原子恢复。prepare_write();这个方法会返回redo log的大小。- 如果
prepare_write();返回的的长度大于0接下来调用log_buffer_reserve()获取handle。
log_buffer_reserve() 首先提前计算插入redo buffer的start_sn 和 end_lsn 。(这是从MySQL 8.0开始,设计了一套无锁的写log机制。)算完
start_sn 和end_lsn 判断是否比buffer的limit大等待之前日志写入回收空间
对于整个日志长度大于当前整个
redo log buffer 的 redo log 需要Resize 设置 redo log bufferm_impl->m_log.for_each_block(write_log);按块写入bufferlog_buffer_write()调用std::memcpy(ptr,str, len)进行写入redo buffer 。log_buffer_write_completed()更新log_t中的recent_written,即(start_lsnend_lsn)组成的list。add_dirty_blocks_to_flush_list()假如产生了 redo log,则将数据页的newest_modification修改为end_lsn。- 假如该 Block 是第一次被修改,就需要插入 Buffer Pool 的
flush_list将涉及修改的数据页添加到 Buffer Pool 的flush_list(buf_flush_insert_into_flush_list()).(利用block->page.oldest_modification来判断是否为第一次修改)
对m_log中的每一个 512 字节的 Block 调用
mtr_write_log_t()(需要注意的是mtr_write_log_t()是运算符()的重载)log_flusher
通知log_flusher线程,log_flusher 线程会调
fsync 将REDO刷盘,至此完成了REDO完整的写入过程。默认情况下
innodb_flush_log_at_trx_commit = 1,线程会block 等待需要等REDO完成刷盘。innodb_flush_log_at_trx_commit = 2,只REDO只写入系统Page Cache。