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,这就是两阶段提交。

相关推荐
hummhumm31 分钟前
Oracle 第19章:高级查询技术
java·数据库·python·sql·mysql·oracle·database
古月~44 分钟前
mysql error:1449权限问题 及 用户授权
数据库·mysql
小强签名设计1 小时前
Flink CDC 同步 Mysql 数据
大数据·mysql·flink
一心赚狗粮的宇叔2 小时前
oracle使用CTE递归分解字符串
mysql·oracle·c#·database
是桃萌萌鸭~3 小时前
导出 MySQL 中所有表的结构(包括外键约束),并在另一个地方创建相同的表
数据库·mysql
KELLENSHAW3 小时前
MySQL45讲 第十六讲 “order by”是怎么工作的?
数据库·mysql
hummhumm3 小时前
Oracle 第20章:数据库调优
java·数据库·后端·python·mysql·oracle·database
A_cot4 小时前
深入理解 MyBatis:从创建到使用与核心知识点
java·spring boot·sql·mysql·spring·maven·mybatis
大嘴吧Lucy5 小时前
实战攻略 | ClickHouse优化之FINAL查询加速
数据库·mysql·clickhouse
颇有几分姿色5 小时前
解析 MySQL 数据库容量统计、存储限制与优化技巧
数据库·mysql