redo log、undo log、bin log详解

redo log、undo log、bin log详解

目录

引言

我们知道一条select语句的执行过程如下图所示
在这里插入图片描述

那么,执行一条update语句期间发生了什么?

执行步骤和查询语句是类似的

  1. 客户端先通过连接器建立连接,连接器会判断用户身份
  2. update语句不需要经过查询缓存,但是表上有更新语句的话,是会把整个表的查询缓存清空的,所以查询缓存在MySQL 8.0 被移除了
  3. 解析器会通过词法分析识别出关键字update,表名等。构建出语法树,接下来会做语法分析,判断输入的语句是否符合MySQL语法。
  4. 预处理器会判断表和字段是否存在
  5. 优化器确定执行计划,如果where条件中id是主键索引,会决定使用id这个索引
  6. 执行器负责具体执行,找到此行,然后更新。

但是update还会涉及到几个重要日志文件:redo log、bin log、undo log。

1. redo log

1.1 为什么需要redo log ?

update包含两步操作,先查询到对应的行记录,再根据条件进行更新操作。如果没有redo log的话,MySQL每次的update操作都要更新磁盘文件,整个过程的I/O成本和查询成本都很高。

1.2 redo log的作用?
  1. 为了防止断电导致的数据丢失的问题,当有一条记录需要更新的时候,InnoDB引擎就会先更新内存(同时标记为脏页),然后将本次对这个页的修改以 redo log的形式记录下来,这个时候更新就算完成了
  2. 将写操作从随机写变成了顺序写

后续,InnoDB引擎会在适当的时候,由后台线程将缓存在Buffer Pool的脏页刷新到磁盘里,这就是**WAL(Write-Ahead Logging)**技术。

✨**WAL**技术指的是,MySQL的写操作并不是立刻写到磁盘上,而是先写日志,然后在合适的时间再写到磁盘上。

1.3 什么是redo log?

redo log是物理日志,记录了某个数据页做了什么修改,比如对XXX表空间中的YYY数据页ZZZ偏移量的地方做了AAA更新。

每当执行一个事务就会产生一条或者多条这样的物理日志。

在事务提交时,只要先将redo log持久化到磁盘即可,可以不需要等到将缓存在Buffer Pool里的脏页数据持久化到磁盘。

当系统崩溃时,虽然脏页数据没有持久化,但是redo log已经持久化,在MySQL重启后,可以根据redo log的内容,将所有数据恢复到最新的状态。

1.4 redo log要写入到磁盘,数据也要写磁盘,为什么要多次一举

写入redo log的方式使用了追加操作,所以操作顺序是顺序写,写入数据是随机写,需要先找到写入位置,然后才写到磁盘。

磁盘的顺序写比随机写高效的多,因此 redo log写入磁盘的开销更小。

WAL技术的另外一个优点:MySQL的写操作从磁盘的随机写变成了顺序写。

1.5 产生的 redo log是直接写入磁盘的吗?

不是的

在执行一个事务的过程中,产生的 redo log也不是直接写入磁盘的,因为这样会产生大量的I/O操作,而且磁盘的运行速度远慢于内存。

redo log也有自己的缓存——redo log buffer,每当产生一条redo log时,会先写入到redo log buffer,后续再持久化到磁盘

在这里插入图片描述

redo log buffer默认大小16MB,可以通过 **innodb_log_Buffer_size**参数动态的调整大小,增大它的大小可以让MySQL处理大事务的时候不必写入磁盘,进而提升写IO性能

1.6 redo log什么时候刷盘?

redo log buffer中的redo log还是在内存中,什么时候刷新到磁盘?

  • MySQL正常关闭时。
  • 当redo log buffer中记录写入量大于其内存空间的一半的时候,会触发落盘。
  • InnoDB的后台线程每隔1s,将redo log buffer持久化到磁盘。
  • 每次事务提交时都将缓存在 redo log buffer 里的 redo log 直接持久化到磁盘。
1.7 redo log文件写满了怎么办

默认情况下,**InnoDB**存储引擎有1个重做日志文件组,由2个redo log文件组成,这两个redo日志的文件名是 **ib_logfile0 **和 ib_logfile1

在这里插入图片描述

在重做日志组中,每个 redo log File 的大小是固定且一致的,假设每个 redo log File 设置的上限是 1 GB,那么总共就可以记录 2GB 的操作。

重做日志文件组是以循环写的方式工作的,从头开始写,写到末尾就又回到开头,相当于一个环形。

如果随着系统运行,Buffer Pool 的脏页刷新到了磁盘中,那么 redo log 对应的记录也就没用了,这时候我们擦除这些旧记录,以腾出空间记录新的更新操作。

redo log 是循环写的方式,相当于一个环形,InnoDBwrite pos 表示 redo log 当前记录写到的位置,用 **checkpoint **表示当前要擦除的位置,如下图:

在这里插入图片描述

如果**write pos 追上了checkpoint,就意味着redo log文件满了,这时MySQL不能再执行新的更新操作,也就是MySQL会被阻塞 **,此时会停下将Buffer Pool中的脏页刷新到磁盘中,然后标记redo log哪些记录可以被擦除,接着对旧的redo log记录进行擦除,等擦除完了旧记录腾出了空间,checkpoint就会往后移动。

因此针对并发量大的系统,适当设置redo log的文件大小非常重要

2. binlog

2.1 binlog是什么

上面提到的redo log是执行引擎层的log文件,binlogServer层面的log文件,也是就是所有执行引擎都有binlog

2.2 为什么InnoDB有一份log文件,MySQL还需要一份log文件呢

这个跟MySQL的时间线有关系

最开始MySQL里并没有InnoDB引擎,MySQL自带的是**MyISAM,但是MyISAM没有crash-safe的能力,binlog**日志只能用于归档

InnoDB 是另一个公司以插件形式引入 MySQL 的,既然只依靠 binlog 是没有 crash-safe 能力的,所以 InnoDB** 使用 redo log 来实现 crash-safe 能力。** ​

2.3 redo logbinlog有什么区别?
  1. 适用对象不同
    • binlog是MySQL的server层实现的日志,所有的存储引擎都可以使用
    • redo log是**InnoDB**存储引擎实现的日志
  2. 文件格式不同
  3. 写入方式不同
    • binlog追加写,写满一个文件,就创建一个新的文件继续写,不会覆盖以前的日志,保存的是全量的日志
    • redo log是循环写,日志空间大小是固定的,全部写满就从头开始,保存未被刷盘的脏页日志
  4. 用途不同
    • binlog 用于备份恢复、主从复制
    • redo log用于掉电等故障恢复
  5. redo log是物理日志,记录的是 在某个数据页做了什么修改,而binlog逻辑日志,记录的是语句的原始逻辑。
2.4 如果不小心整个数据库的数据被删除了,能使用 redo log文件恢复数据吗?

不能,只能使用**binlog,因为binlog中保存的是全量**的日志,而redo log不是。

2.5 主从复制是怎么实现的?

MySQL的主从复制依赖于binlogbinlog记录数据库中的所有变化并以二进制的形式保存在磁盘上,复制的过程就是将**binlog**中的数据从主库传输到从库上。

✨这个过程一般是异步

在这里插入图片描述

MySQL集群的主从复制过程可以整理成3个阶段

  • 写入**binlog:主库写入binlog**日志,提交事务并更新本地存储数据。
  • 同步**binlog:把binlog复制到所有从库上,每个从库把binlog写到暂存日志中 **。
  • 回放**binlog**:从库回放binlog,并更新存储引擎中的数据。

具体详细的过程如下

  • MySQL主库在收到客户端提交事务的请求之后,会先写入binlog,再提交事务,更新存储引擎中的数据,事务提交完成后,返回给客户端操作成功的响应。
  • 从库会创建一个专门的I/O线程,连接主库的log dump线程,来接收主库的binlog日志,再把binlog信息写入relay log的中继日志中,然后返回给主库复制成功的响应。
  • 从库会创建一个用于回放binlog的线程,去读relay log中继日志,然后回访binlog更新存储引擎中的数据,最终实现主从的数据一致性。

在完成主从复制后,你就可以在写数据时只写主库,在读数据时只读从库,这样即使写请求会锁表或者锁记录,也不会影响读请求的执行。

在这里插入图片描述

Redis的主从复制过程是如何进行的呢?

2.6 从库是否是越多越好?

答案是否定的。

因为从库数量增加,从库连接上来的I/O线程也会更多,主库需要创建同样多的log dump线程来处理复制的请求,对主库资源消耗比较高,同时还受限于主库的网络带宽。

2.7 binlog什么时候刷盘?

事务执行过程中,先把日志写到**binlog cache(Server层的cache) **,事务提交的时候,再把binlog cache写到binlog文件中。

✨一个事务的binlog是不能被拆开的

无论这个事务有多大,也要保证一次性写入,因为一个线程同时只能由一个事务在执行,所以每当执行一个begin/start transaction的时候,就会默认提交上一个事务,这样**如果一个事务的binlog**被拆开的时候,在备库执行的时候就会被当作多个事务分段执行,这样破坏了原子性,是有问题的。

2.8 什么时候binlog cache会写入到binlog文件

事务提交的时候

在这里插入图片描述

✨虽然每个线程有自己 binlog cache,但是最终都写到同一个**binlog** 文件:

  • 图中的 write,指的就是指把日志写入到 binlog 文件,但是并没有把数据持久化到磁盘,因为数据还缓存在文件系统的 page cache 里,write 的写入速度还是比较快的,因为不涉及磁盘 I/O。
  • 图中的 fsync,才是将数据持久化到磁盘的操作,这里就会涉及磁盘 I/O,所以频繁的 fsync 会导致磁盘的 I/O 升高。

3. Buffer Pool

3.1 为什么需要Buffer Pool

Innodb存储引擎设计了一个缓存池,来提高数据库的读写性能。

更新一条记录的时候,先缓存起来,这样下次有查询语句命中了这条记录,可以直接读取缓存中的记录,就不需要从磁盘中获取数据了。

3.2 Buffer Pool会缓存什么

Buffer Pool 除了缓存「索引页」和「数据页」,还包括了 Undo 页,插入缓存、自适应哈希索引、锁信息等等。

在这里插入图片描述

MySQL 刚启动的时候,你会观察到使用的虚拟内存空间很大,而使用到的物理内存空间却很小,这是因为只有这些虚拟内存被访问后,操作系统才会触发缺页中断,申请物理内存,接着将虚拟地址和物理地址建立映射关系。

4. undo log

4.1 为什么需要undo log ?

在执行一条 增删改 语句的时候,虽然没有输入begin开始事务和commit提交事务,但是MySQL会隐式开启事务来执行增删改语句的。

执行一条语句是否自动提交事务,是由**autocommit**参数决定的,默认是开启的

undo log(回滚日志),保证了事务ACID特性中的原子性

undo log是一种用于撤销回退的日志。在事务没提交之前,MySQL会先记录更新前的数据到undo log日志文件里面,当事务回滚时,可以利用undo log来进行回滚。

在这里插入图片描述

每当 InnoDB 引擎对一条记录进行操作(修改、删除、新增)时,要把回滚时需要的信息都记录到 undo log 里

不同的操作,需要记录的内容也是不同的,所以不同类型的操作(修改、删除、新增)产生的 undo log 的格式也是不同的。

4.2 版本链

一条记录的每一次更新操作产生的undo log格式都有一个**roll_pointer指针和一个trx_id**事务id。

  • 通过**trx_id**可以知道该记录是被哪个事务修改的
  • 通过**roll_pointer指针可以将这些undo log串成一个链表,这个链表就被称为版本链 **。

在这里插入图片描述

4.3 多版本并发控制(MVCC)

undo log还有一个作用,通过**ReadView + undo log**实现多版本并发控制

对于读已提交和可重复度隔离级别的事务来说,它们的快照读(普通的select语句)是通过**ReadView + undo log**来实现的,区别在于创建Read View 的时机不同

  • 读已提交隔离级别是在每个select都会生成一个新的Read View,也就意味着,事务期间的多次读取同一条数据,前后两次读的数据可能会出现不一致,因为可能这期间另外一个事务修改了该记录,并提交了事务。
  • 可重复读隔离级别是启动事务时生成一个Read View,然后整个事务期间都在用这个Read View,这样就保证了事务期间读到的数据都是事务启动前的记录。

这两个隔离级别的实现是通过事务的Read View里的字段undo log记录中的两个隐藏列(trx_idroll_pointer的对比,如果不满足可见行,就会顺着undo log版本链里找到满足其可见性的记录,从而控制并发事务访问同一个记录时的行为,就叫MVCC

4.4 undo log的两大作用
  • 实现事务的回滚,保障事务的原子性
  • 实现**MVCC**的关键因素之一

5. 两阶段提交

5.1 为什么需要两阶段提交?

事务提交后,redo logbinlog都要持久化到磁盘,但这两个是独立的逻辑,可能出现版成功的状态,这样就造成两份日志之间的逻辑不一致。

  • 如果在redo log刷入磁盘之后,MySQL突然宕机了,而binlog还没有来得及写入。
  • 如果在将binlog刷入磁盘之后,MySQL突然宕机了,而redo log还没来得及写入。也会导致主从不一致
5.2 两阶段提交的过程

✨将redo log的写入拆成了两个步骤:prepare和commit,中间再穿插写入binlog

5.3 两阶段提交有什么问题

虽然保证了两个日志文件的数据一致性,但性能很差。

  • 磁盘I/O次数高
  • 锁竞争激烈。 两阶段提交虽然能保证单事务的两个日志的内容一致,但在多事务的情况下,却不能保证两者的提交顺序一致,因此,在两阶段提交的流程基础上,还需要加一个锁来保证提交的原子性。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值