MySQL数据库(binlog,redolog,undolog)

binlog 、redo log和undo log这三种日志在MySQL中是经常被提到的三种日志,无论在平时面试中还是在工作中,都会或多或少有所涉及。在这里,做一个尽可能全面的总结,让读者能够一篇通读下来,可以掌握好这三种日志。

MySQL的分层架构图

众所周知,MySQL的架构分为多层,如上图所示,MySQL的具体分层描述不在这里做详细解释,在后续文章中在做分解。

这里直接说明:binlog 是server层实现的日志,而redo log 和undo log都是引擎层(innodb)实现的日志。因为它们分属于不同的层级体系中,所以他们所能用来解决问题的目的各不相同,下面我们就来详细讲解下这三种日志的作用以及它们之间的区别。

一、binlog

binlog 是作为mysql操作记录归档的日志,这个日志记录了所有对数据库的数据、表结构、索引等等变更的操作。也就是说只要是对数据库有变更的操作都会记录到binlog里面来, 可以把数据库的数据当成我们银行账户里的余额,而binlog就相当于我们银行卡的流水。账户余额只是一个结果,至于这个结果怎么来的,那就必须得看流水了。而同样在mysql里我们就是通过binlog来归档、验证、恢复、同步数据。

binlog 记录内容

binlog应该说是MySQL里最核心的日志, 它记录了除了查询语句(select、show)之外的所有的 DDL 和 DML 语句,也就意味着我们基本上所有对数据库的操作变更都会记录到binlog里面。binlog以事件形式记录,不仅记录了操作的语句,同时还记录了语句所执行的消耗的时间。 binlog 有三种记录格式,分别是ROW、STATEMENT、MIXED。

  1. statement(5.6默认)SBR(statement based replication) :语句模式原封不动的记录当前DML。
  2. ROW(5.7 默认值) RBR(ROW based replication) :记录数据行的变化(用户看不懂,需要工具分析)
  3. mixed(混合)MBR(mixed based replication)模式 :以上两种模式的混合

SBR与RBR模式的对比

  • STATEMENT:可读性较高,日志量少,但是不够严谨
  • ROW :可读性很低,日志量大,足够严谨

市面上一些成熟的工具和解决方案,也是通过解析binlog来实现数据同步与分析,这块在日常面试中,可能会被面试官问到一些实际的应用场景,有条件的读者可以自行查阅相关资料或者咨询自己公司的中间件团队。

笔者在实际工作中,接触过kafka-binlog和canal等相关解决方案来处理工作中的MySQL日志数据。

二、redo log (重做日志又称为前滚日志)

首先,MySQL 数据是存在磁盘中的,每次读写数据都需要做磁盘随机IO,因此并发场景下MySQL性能是比较差的。为此 MySQL 引入缓存(Buffer Pool)做优化,磁盘中部分数据页(page)与对应的缓存(Buffer Pool)进行映射,以此来缓解数据库的磁盘压力。

当从数据库读数据时,会先从缓存(Buffer Pool)中读,缓存(Buffer Pool)中没有,则从磁盘读后放入缓存(Buffer Pool);当向数据库写数据时,先向缓存(Buffer Pool)中写,此时缓存(Buffer Pool)中的数据页数据会变更,该数据页叫脏页,Buffer Pool 中修改完数据后会按照设定的策略再定期刷到磁盘中去,这个过程叫刷脏页

那么问题来了,如果 Buffer Pool 中修改的数据还没有及时的刷到磁盘,MySQL 宕机重启,就会导致数据丢失,无法保证事务的持久性,怎么办?(好问题)

回答:redo log 解决了这个问题。

当数据库在修改数据时,会把更新记录先写到 redo log 中,再去修改 Buffer Pool 中的数据,当提交事务时,调用 fsync 把 redo log 刷入磁盘。至于缓存中更新的数据文件何时刷入磁盘,则由后台线程异步处理。

MySQL数据更新的流程(两阶段提交)

下面详细拆解一下图中容易产生疑问的几个点:

首先,当用户对数据进行变更操作时,在数据真正变更之前,存储引擎层会先将当前数据保存在undolog中形成一个历史版本,以便回滚的时候使用。

其次,在将脏页刷新到磁盘之前,存储引擎层会先将变更数据的记录写入到redo log buffer中,并且通过顺序IO刷新到磁盘当中的redolog中,将redolog的状态置为prepare(这里其实就相当于告诉Binlog,redolog已经记录好数据变化了,可以开始更新了)。

然后,接下来server层会进行binlog日志文件的更新,将数据变化写入到binlog中,当binlog更新完成后事务才算成功commit,并将commit这个状态写入到redolog中。

最后,在执行刷脏页这个操作,这个刷脏页的操作是随机IO

这里有的小伙伴或许会有疑问:上诉过程中为什么redo log file要分为两个状态?

答:是为了保证redolog和binlog的数据一致性。

详细分析:

如果redo log写失败了,而binlog写成功了。那假设内存的数据还没来得及落磁盘,机器宕机,那主从服务器的数据就不一致了(从服务器通过binlog得到最新的数据,而主服务器由于redo log没有记载,没法恢复数据),所以如果redo log写失败了,那我们就认为这次事务有问题,回滚,不再写binlog。

如果redo log写成功了,而binlog写失败了,主从将无法同步,所以我们还是会对这次的事务进行回滚操作,将无效的binlog给删除(因为binlog会影响从库的数据,所以需要做删除操作)。

redo log写入策略

由innodb_flush_log_at_trx_commit 参数决定。

innodb_flush_log_at_trx_commit=1,表示在每次事务提交的时候,都把log buffer刷到文件系统中(os buffer)去,并且调用文件系统的“flush”操作将缓存刷新到磁盘上去。这样的话,数据库对IO的要求就非常高了,如果底层的硬件提供的IOPS比较差,那么MySQL数据库的并发很快就会由于硬件IO的问题而无法提升。

innodb_flush_log_at_trx_commit=0 ,表示每隔一秒把log buffer刷到文件系统中(os buffer)去,并且调用文件系统的“flush”操作将缓存刷新到磁盘上去。也就是说一秒之前的日志都保存在日志缓冲区,也就是内存上,如果机器宕掉,可能丢失1秒的事务数据。

innodb_flush_log_at_trx_commit=2,表示在每次事务提交的时候会把log buffer刷到文件系统中去,但并不会立即刷写到磁盘。如果只是MySQL数据库挂掉了,由于文件系统没有问题,那么对应的事务数据并没有丢失。只有在数据库所在的主机操作系统损坏或者突然掉电的情况下,数据库的事务数据可能丢失1秒之类的事务数据。这样的好处,减少了事务数据丢失的概率,而对底层硬件的IO要求也没有那么高(log buffer写到文件系统中,一般只是从log buffer的内存转移的文件系统的内存缓存中,对底层IO没有压力)。

具体可进到终端里面:SQL语句:select @@innodb_flush_log_at_trx_commit;

redo log 的写入方式?

redo log 采用大小固定,循环写入的方式,当写满后,会重新从头开始循环写,类似一个环状。这样设计原因是 redo log 记录的是数据页上的修改,如果 Buffer Pool 中数据页已经刷到磁盘,这些记录就失效了,新日志会将这些失效的记录覆盖擦除。
注意:redo log 满了,在擦除之前,要确保这些要被擦除记录都已经刷到磁盘中了。在擦除旧记录释放新空间期间,不能再接收新的更新请求,此时 MySQL 性能会下降。因此高并发情况下,合理调整 redo log 大小很重要。

redo Log环形图

crash-safe 能力是什么?

Innodb 引擎有 crash-safe 能力,即事务提交过程中任何阶段,MySQL 宕机重启后都能保证事务的完整性,已提交的数据不会丢失。这种能力是通过redo log保证的,MySQL 宕机重启,系统将自动检查 redo log,将修改还未写入磁盘的数据从 redo log 恢复到 MySQL 中。

三、undo log

undo log 是也属于引擎层(innodb)的日志,从上面的redo log介绍中我们就已经知道了,redo log 和undo log的核心是为了保证innodb事务机制中的持久性和原子性,事务提交成功由redo log保证数据持久性,而事务可以进行回滚从而保证事务操作原子性则是通过undo log 来保证的。

要对事务数据回滚到历史的数据状态,所以我们也能猜到undo log是保存的是数据的历史版本,通过历史版本让数据在任何时候都可以回滚到某一个事务开始之前的状态。
undo log除了进行事务回滚的日志外还有一个作用,就是为数据库实现MVCC多版本并发控制的功能。

四、啥是回滚和前滚?

(1)回滚
未提交的事务,即事务未执行 commit。但事务内修改的脏页中,有一部分已刷盘。此时数据库宕机重启,需要回滚来将先前那部分已经刷盘的脏块从磁盘上撤销。
(2)前滚
未完全提交的事务,即事务已经执行 commit,但该事务内修改的脏页中只有一部分数据被刷盘,另一部分还在 buffer pool,此时数据库宕机重启,就要用前滚来将未来得及刷盘的数据从 redo log 中恢复出来并刷盘。

undo log记录内容

在Mysql里数据每次修改前,都首先会把修改之前的数据作为历史保存一份到undo log里面的,数据里面会记录操作该数据的事务ID,然后我们可以通过事务ID来对数据进行回滚。

作者水平有限,只能尽量按照自己的经验和查阅相关资料总结后整理出相关文章,如文章中有不同异议的,可留言或私信作者,以期共同进步。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值