redolog与binlog的写入机制

redo log

事务在执行的过程中,生成的redo log是要先写到redo log buffer中的。redo log buffer里面的内容不需要每次生成后都直接持久化到磁盘。

如果事务执行期间MySQL发生异常重启,那这部分日志就丢了,但是由于没有commit,所以丢了也不会有损失。

不过,事务没commit时,redo log buffer却有可能持久化到磁盘

redo log可能存在的三个状态

  1. 存在redo log buffer中,物理是在MySQL进程中
  2. 写到磁盘(write),但是没有持久化(fsync),物理上是在文件系统的page cache中
  3. 持久化到磁盘,物理上是在hard disk中

写redo log buffer是很快的,write到page chche也差不多,但是fsync的速度就慢很多了

InnoDB提供了innodb_flush_log_at_trx_commit参数用于控制redo log的写入策略

  1. 设置为0,每次commit都只把redo log留在redo log buffer
  2. 设置为1,每次commit都将redo log持久化到hard disk
  3. 设置为2,每次commit都只把redo log写到page cache

InnoDB有一个后台线程,每间隔1s就会把redo log buffer中的日志,调用write写到page cache,然后调用fsync持久化到hard disk

注意,事务执行中的redo log也是直接写到redo log buffer中的,这些redo log也会被一起持久化到hard disk。也就是说一个没有commit的事务的redo log也是可能已经fsync到hard disk的

除此以外,还有两种场景会让没有commit的事务的redo log fsync到hard disk中

  1. redo log buffer占用空间即将达到innodb_log_buffer_size一半的时候,后台线程会主动写盘。只是write,没有fsync
  2. 并行的事务commit时,顺带将这个事务的redo log buffer持久化到hard disk

binlog

事务执行过程中,先把日志写到binlog cache,事务commit时,再把binlog cache写到binlog文件中

一个binlog是不能被拆开的,因此无论事务多大也要一次性写入。这也就涉及到了binlog cache的保存问题

系统给binlog cache分配了一片内存,每个线程一个,参数binlog_cache_size控制单个线程内binlog cache所占内存的大小。如果超过了这个参数,就要暂存到磁盘。

事务commit时,执行器把binlog cache里的完成事务写到binlog中,并情况binlog cache

每个线程有自己的binlog cache,但是共用同一份binlog文件

  1. write操作是写入page cache,没有把数据持久化到hard disk,速度较快
  2. fsync操作,将数据持久化到hard disk。占磁盘的IOPS

write和fsync的时机,是由参数sync_binlog控制的

  1. 设置为0时,每次只write,不sync
  2. 设置为1时,每次都fsync
  3. 设置为N时,表示每次都write,累积N个后fsync

因此,在出现IO瓶颈的场景中,将sync_binlog设置为一个比较大的值,一般为100---1000,对应的风险是有可能会丢失最近的N个日志

binlog与redo log的区别

  1. redo log是InnoDB引擎持有的,binlog是MySQL的Server层实现的,所有引擎都可以使用
  2. redo log是物理日志,记录的是"在某个数据页上做了什么修改";binlog是逻辑日志,记录的是这个语句的原始逻辑,比如"给ID为2这一行的c字段+1"
  3. redo log是循环写的,空间固定会用完;binlog是可以追加写入的

有了对这两个日志的概念性理解,执行这个update语句的内部流程

  1. 执行器先找引擎取ID=2这一行。ID是主键,引擎直接用树搜索到这一行。如果ID=2这一行所有的数据页本来就在内存中,就直接返回给执行器;否则,需要先从磁盘读入内存,然后再返回。
  2. 执行器拿到引擎给的行数据,把这个值加上1,得到一行新的数据,再调用引擎接口写入这行新数据。
  3. 引擎将这行新数据更新到内存中,同时将这个更新操作记录到redo log里面,此时redo log处于prepare状态。然后告知执行器执行完成了,随时可以提交事务。
  4. 执行器生成这个操作的binlog,并把binlog写入磁盘。
  5. 执行器调用引擎的提交事务接口,引擎把刚刚写入的redo log改成提交(commit)状态,更新完成。

流程图如下,浅色表示在InnoDB内部执行,深色表示在执行器中执行。

最后三步将redo log的写入拆成了两个步骤:prepare和commit,这就是两阶段提交。

相关推荐
JavaGuide2 小时前
公司来的新人用字符串存储日期,被组长怒怼了...
后端·mysql
怒放吧德德5 小时前
MySQL篇:MySQL主从集群同步延迟问题
后端·mysql·面试
Eip不易也不e6 小时前
教程之同时安装两个版本的 mysql
mysql
Kagol7 小时前
macOS 和 Windows 操作系统下如何安装和启动 MySQL / Redis 数据库
redis·后端·mysql
Qi妙代码9 小时前
MYSQL基础
数据库·mysql·oracle
llzcxdb9 小时前
【MySQL】理解MySQL的双重缓冲机制:Buffer Pool与Redo Log的协同之道
数据库·mysql
Allen Bright10 小时前
【MySQL基础-20】MySQL条件函数全面解析:提升查询逻辑的利器
数据库·mysql
dleei11 小时前
MySql安装及SQL语句
数据库·后端·mysql
信徒_12 小时前
Mysql 在什么样的情况下会产生死锁?
android·数据库·mysql
苹果酱056713 小时前
Golang标准库——runtime
java·vue.js·spring boot·mysql·课程设计