mysql的redo log

发布时间 2023-11-10 09:47:08作者: 朝阳1
 所谓两阶段提交,其实就是把 redo log 的写入拆分成了两个步骤:prepare 和 commit。
    首先,存储引擎将执行更新好的新数据存到内存中,同时将这个更新操作记录到 redo log 里面,此时 redo log 
    处于 prepare 状态。然后告知执行器执行完成了,随时可以提交事务
    然后执行器生成这个操作的 bin log,并把 bin log 写入磁盘
    最后执行器调用存储引擎的提交事务接口,存储引擎把刚刚写入的 redo log 状态改成提交(commit)状态,更新完成

    如果数据库在写入 redo log(prepare) 阶段之后、写入 binlog 之前,发生了崩溃:
    此时 redo log 里面的事务处于 prepare 状态,binlog 还没写,之后从库进行同步的时候,无法执行这个操作,
    但是实际上主库已经完成了这个操作,所以为了主备一致,MySQL 崩溃时会在主库上回滚这个事务
    而如果数据库在写入 binlog 之后,redo log 状态修改为 commit 前发生崩溃,此时 redo log 
    里面的事务仍然是 prepare 状态,binlog 存在并完整,这样之后就会被从库同步过去,
    但是实际上主库并没有完成这个操作,所以为了主备一致,即使在这个时刻数据库崩溃了,主库上事务仍然会被正常提交。

也就是说,redolog 其实存在三种状态:

    事务执行过程中,存在 MySQL 的进程内存中的 redolog buffer 中
    事务提交,执行 write 操作存在文件系统的 page cache 中,但是没有执行 fsync 操作持久化到磁盘
    事务提交,执行 fsync 操作持久化到磁盘

为什么说事务还没提交的时候,redolog 也有可能被持久化到磁盘呢?

主要有三种可能的原因:

    第一种情况:InnoDB 有一个后台线程,每隔 1 秒轮询一次,具体的操作是这样的:调用 write 将 
    redolog buffer 中的日志写到文件系统的 page cache,然后调用 fsync 持久化到磁盘。而在事务执行中间过程的 
    redolog 都是直接写在 redolog buffer 中的,
    也就是说,一个没有提交的事务的 redolog,也是有可能会被后台线程一起持久化到磁盘的。
    第二种情况:
    innodb_flush_log_at_trx_commit 设置是 1,这个参数的意思就是,每次事务提交的时候,都执行
    fsync 将 redolog 直接持久化到磁盘(还有 0 和 2 的选择,0 表示每次事务提交的时候,都只是把 redolog 留在
    redolog buffer 中;
    2 表示每次事务提交的时候,
    都只执行 write 将 redolog 写到文件系统的 
    page cache 中)。
    举个例子,假设事务 A 执行到一半,已经写了一些 redolog 到 redolog buffer 中,这时候有另外一个事务 B 提交
    ,按照 innodb_flush_log_at_trx_commit = 1 的逻辑,事务 B 要把 redolog buffer 里的日志全部持久化到磁盘,
    这时候,就会带上事务 A 在 redolog buffer 里的日志一起持久化到磁盘
    第三种情况:redo log buffer 占用的空间达到 redolo buffer 大小(由参数 innodb_log_buffer_size 控制,
    默认是 8MB)一半的时候,后台线程会主动写盘。
    不过由于这个事务并没有提交,所以这个写盘动作只是 write 到了文件系统的
    page cache,仍然是在内存中,并没有调用 fsync 真正落盘

为什么要有 redo log ?

我们都清楚,事务的四大特性其中有一个是持久性,简单的说就是只要事务提交成功,对数据库做的修改就会被永久保存下来,不会因为任何原因再回到原来的状态。

MySQL 是怎么样保证持久性的呢?最简单的做法是在每次事务提交的时候,将该事务涉及修改的数据页全部刷新回磁盘中,可是这么做存在严重的性能问题:

单个事务可能涉及修改多个数据页,并且数据页在物理上并不连续,使用随机IO写入性能太差。
Innodb是以页为单位进行磁盘交互的,一个事务有可能只会修改一个数据页中的几个字节,如果这时候将完整的数据页刷回磁盘的话,很浪费资源。
因此 MySQL 设计出了redo log,当一条记录更新的时候, InnoDB 引擎会先把记录写到 redo log 里面去,同时更新内存,这样就算这条数据更新成功了,完美地解决了性能问题(文件更小并且是顺序IO)。

注意此时数据并没有更新到磁盘上,InnoDB 会在恰当的时候把这条记录更新到磁盘上去。这种先写日志然后再将数据刷盘的机制,有个专有名词——WAL(Write-ahead logging)。

redo log 如何刷到磁盘的呢?

redo log包含两部分:

内存中的日志缓冲(redo log buffer)
磁盘上的日志文件(redo log file)
每执行一条DML语句,数据库先将记录写入redo log buffer,然后后续某个时间点再一次性将多个操作记录写到redo log file。MySQL 一共支持三种写入redo log file的时机,通过参数 innodb_flush_log_at_trx_commit 进行配置,如下图所示: