为什么有binlog还要redo log

什么是WAL

MySQL(innodb引擎)中的WAL技术的全称是Write-Ahead-Logging,它的关键点是先写日志,再写磁盘;

MySQL中更新一条语句的流程

(深色service层执行器,浅色存储引擎)
在这里插入图片描述
具体流程:

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

两阶段提交中,MySQL异常重启(crash),是如何保证数据完整性的?

在这里插入图片描述

  1. 在上图时刻A中,也就是写入redo log处于prepare阶段以后、写binlog之前,发生了崩溃(crash):由于此时binlog还没写,redo log也还没提交(commit),所以崩溃恢复的时候,这个事务会回滚。这时候,binlog还没写,所以也不会传到备库,数据一致;
  2. 在上图时刻B中,也就是写完binlog之后,发生crash:如果redo log里面的事务有commit标识(事务是完整的)则直接提交;如果redo log里面的事务只有prepare没有commit,则判断对应的事务在binlog是否存在并完整,完整则提交事务,否则回滚事务;
  3. MySQL怎么知道binlog是完整的:一个事务的binlog是有完整格式的,statement格式(记录sql语句),最后会有个COMMIT; row格式(记录行的内容,记两条,更新前和更新后都有),最后会有个XID event
  4. redo log和binlog是怎么关联起来的:他们有一个共同的数据字段XID。崩溃恢复的时候,会按顺序扫描redo log;上述第二点中的崩溃恢复场景(redo log里面的事务只有完整的prepare而没有commit),那么mysql就会拿着XID去binlog找是否存在对应的完整事务;

为什么不仅使用binlog来支持崩溃恢复,并且还能支持归档

  1. 历史原因:innodb并不是MySQL的原生存储引擎。MySQL的原生引擎是myISAM,设计之初就没有支持崩溃恢复。
  2. 实现原因:由于innodb引擎使用的是WAL技术,执行事务的时候,写完内存和日志,事务就算完成了。并且binlog没有能力恢复“数据页”,所以仅使用binlog的话,当发生了crash之后,是无法凭借binlog把那些已经commit过的事务进行恢复的;
  3. 优化一下binlog的实现,让它也记录数据页的更改:这相当于又做了一个redo log出来!

为什么不仅使用redo log,不要binlog可以吗?

  1. 如果只从崩溃恢复的角度来讲是可以的。可以把binlog关掉,但系统依然是crash-safe的;
  2. 但是binlog有着redo log无法替代的功能,binlog的主要作用是归档,redo log是循环写,写到末尾是要回到开头继续写的。
  3. MySQL系统依赖于binlog,mysql系统高可用的基础就是依赖于binlog复制;

redo log到底是什么,数据最终落盘,是从redo log更新来的吗?

  1. redo log只是记录了数据页上的改动,并没有记录数据页的完整数据,所以redo log并没有能力更新磁盘数据;
  2. 脏页:正常运行的实例中,当数据页被修改之后,和磁盘的数据页不一致,称为脏页;
  3. 数据落盘指的就是把内存中的数据页(脏页)写入磁盘。这个过程,甚至与redo log毫无关系;
  4. 当然如果在崩溃恢复场景中,(脏页)从内存中丢失了更新,那么innodb就会将这个数据页读到内存中,然后通过redo log更新该数据页的内容(crash-safe)。更新完成后,这个数据页就变成了脏页;

redo log buffer是什么?

  1. redo log buffer 就是一块内存,用来暂存redo日志的
  2. 在一个事务的更新过程中,日志是要写多次的。比如下面这个事务:
begin;
insert into t1 ...
insert into t2 ...
commit;

这个事务要往两个表中插入记录,插入数据的过程中,生成的日志都得先保存起来,但又不能在还没 commit 的时候就直接写到 redo log 文件里。也就是说,在执行第一个 insert 的时候,数据的内存被修改了,redo log buffer 也写入了日志。但是,真正把日志写到 redo log 文件(文件名是 ib_logfile+ 数字),可能是在执行 commit 语句的时候做的。

与redo log持久化有关的配置参数innodb_flush_log_at_trx_commit

  1. 设置为 0 的时候,表示每次事务提交时都只是把 redo log 留在 redo log buffer 中 ;
  2. 设置为 1 的时候,表示每次事务提交时都将 redo log 直接持久化到磁盘;
  3. 设置为2 的时候,表示每次事务提交都只是把redolog写到page cache

与binlog持久化有关的配置参数sync_binlog

  1. sync_binlog=0 的时候,表示每次提交事务都只写page cache ,不会持久化到硬盘
  2. sync_binlog=1 的时候,表示每次提交事务之后都会写page cache,并且持久化到硬盘
  3. sync_binlog=N 的时候,表示当积累N次事务之后就会一次性写入硬盘

如果不是什么特别重要的数据,且并发比较高的时候可以把sync_binlog设置成100-1000中的某个值,这样可以提高性能
如果是对特别重要的数据,例如订单数据则建议将sync_binlog的值设置为1,这样能够保证哪怕数据库挂了,也可以保证不丢数据
当sync_binglog设置为0的时候,mysql会根据操作系统自动进行写入且mysql默认这个值就是0

redo log与binlog的区别

  1. redo log是innodb引擎特有的,binlog是MySQL的Server层实现的,所有引擎都可以使用;
  2. redo log是物理日志,记录的是“在某个数据页上做了什么修改”(数据页上某个偏移量的值);binlog是逻辑日志,记录的是这个语句的原始逻辑(sql、数据行)
  3. redo log是循环写的,空间固定会用完,用完就需要刷盘然后从头开始写;binlog是可以追加写入的。“追加写”是指binlog文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。

疑问

  1. 两阶段提交中,当innodb_flush_log_at_trx_commit <> 1(redo log在每次事务提交并不会立刻持久化)
    sync_binlog = 1(binlog 在每次事务提交后立刻就持久化)
    那么在两阶段提交后,事务commit了,然后系统突然断电。此时redo log buffer丢失,并且相关更新并没有持久化到redo log 而只是持久化到binlog。断电恢复之后,mysql是如何进行处理的呢?
  • 15
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 14
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值