在这里插入图片描述

前言

MySQL最为最流行的开源数据库,其重要性不言而喻,也是大多数程序员接触的第一款数据库,深入认识和理解MySQL也比较重要。

本篇博客阐述MySQL的日志,介绍重要的bin log (归档日志) 、 事务日志redo log(重做日志) 、 undo log(回滚日志)。

本系列文章合集如下:

【合集】MySQL的入门进阶强化——从 普通人 到 超级赛亚人 的 华丽转身

在这里插入图片描述

引出


1.二进制日志bin log (归档日志)事务日志redo log(重做日志)undo log(回滚日志)
2.bin log用于备份恢复、主从复制;redo log用于掉电等故障恢复;
3.redo log一旦提交意味着持久化了,但是有时候需要对其进行rollback操作,那就需要undo log;
4.主从:写数据时只写主库,在读数据时只读从库,这样即使写请求会锁表或者锁记录,也不会影响读请求的执行;

一、MySQL日志分类

MySQL日志主要包括错误日志、查询日志、慢查询日志、事务日志、二进制日志几大类。其中,比较重要的还要属二进制日志binlog (归档日志)事务日志redo log(重做日志)undo log(回滚日志)

MySQL日志主要包括七种:

  • 1.重做日志(redo log)
  • 2.回滚日志(undo log)
  • 3.归档日志(bin log)
  • 4.错误日志(error log)
  • 5.慢查询日志(slow query log)
  • 6.一般查询日志(general log)
  • 7.中继日志(relay log)

二、Redo Log

redo log(重做日志)是nnoDB存储引擎独有的,它让MySQL拥有了崩溃恢复能力。比如MySQL实例挂了或宕机了,重启时,InnoDB存储引擎会使用redo log恢复数据

(1)redo log是物理日志,纪录页的物理修改操作

  • 记录的是在某个数据页做了什么修改,比如对x表空间中的N数据页ZZZ偏移量的地方做了AAA更新:

(2)保证数据的持久性

  • 持久性,redo log会在事务提交时将日志存储到磁盘redo log file,保证日志的持久性。同时mysql会将数据写入磁盘,保证数据的持久性。

1. redo log重做日志的组成

  • 一是内存中的重做日志缓存,叫做redo log buffer

  • 二是重做日志文件,叫做redo log file

MySQL中数据是以页为单位,你查询一条记录,会从硬盘把一页的数据加载出来,加载出来的数据叫数据页,会放入到Buffer Pool中。后续的查询都是先从Buffer Pool中找,没有命中再去硬盘加载,减少硬盘IO开销,提升性能。

更新表数据的时候,也是如此,发现Buffer Pool里存在要更新的数据,就直接在Buffer Pool里更新。然后会把在某个数据页上做了什么修改记录到重做日志缓存(redo log buffer)里,接着刷盘到redo log文件里。同时,InnoDB引擎会在适当的时候,将这个操作记录更新到磁盘里面。

redo log的更新流程如下,以一次update操作为例

在这里插入图片描述

在这里插入图片描述

(1)执行Update操作

(2)先将原始数据读从磁盘读取到内存,修改内存中的数据。

(3)生成一条重做日志写入redo log buffer ,纪录数据被修改后的值。

(4)当事物提交时,需要将redo log buffer中的内容刷新到redo log file。

(5)事物提交后,也会将内存中修改的数据写入到磁盘。

为什么需要写Redo Log Buffer 和 Redo Log Flle?

为什么需要写Redo Log Buffer 和 Redo Log Flle?而不是直接持久化到磁盘?

直接写磁盘会有产生严重的性能问题:
(1)InnoDB在磁盘中存储的基本单元是页,可能本次修改只变更一页中几个字节,但是需要刷新整页的数据,就很浪费资源。
(2)一个事务可能修改了多页中的数据,页之间又是不连续的,就会产生随机IO,性能更差。

这种方案叫做WAL(Write-Ahead Logging),预写日志,就是先写日志,再写磁盘.

2. 刷盘的时机

InnoDB存储引擎为redo log的刷盘策略提供了innodb-f1ush_log-at_-trX-commit参数,它支持三种策略:

(1) 0(延迟写) :表示每次事务提交时都只是把redo log留在redo log buffer中,开启一个后台线程,每1s刷新一次到磁盘中;

在这里插入图片描述

(2)1(实时写,实时刷):表示每次事务提交时都将redo log直接持久化到磁盘,真正保证数据的持久性(默认值);

在这里插入图片描述

刷盘的时机为1时,只要事务提交成功,redo log记录就一定在硬盘里,不会有任何数据丢失

如果事务执行期间MySQL挂了或宕机,这部分日志丢了,但是事务并没有提交,所以日志丢了也不会有损失。

(3)2(实时写,延迟刷):表示每次事务提交时都只是把 redo log 写到 page cache,每秒刷一次到磁盘(速度快,但是会丢1s的数据,甚至更多,1s并不严格)

在这里插入图片描述

三、undo log (事务回滚)

redo log一旦提交意味着持久化了,但是有时候需要对其进行rollback操作,那就需要undo log。

undo log是逻辑日志,只是将数据库逻辑的恢复到原来的样子。并不能将数据库物理地恢复到执行语句或者事务之前的样子。虽然所有的逻辑修改均被取消了,但是数据结构和页本身在回滚前后可能不一样了。

既然是逻辑日志,可以理解为它存储的是SQL, 在事务中使用的每一条 INSERT 都对应了一条 DELETE,每一条 UPDATE 也都对应一条相反的 UPDATE 语句。

在这里插入图片描述

undo log实现了事务的一致性,是通过undo log恢复到事务之前的逻辑状态,保证一致性。

在事务没提交之前,MySQL会先记录更新前的数据到undo log日志文件里面,当事务回滚时,可以利用undo log来进行回滚。如下图:

在这里插入图片描述

Undo Logi通过两个隐藏列tx_id(最近一次提交事务的ID)和roll_pointer(上个版本的地址),建立一个版本链,实现回滚到上一个版本。

在这里插入图片描述

四、Bin log

bin log用于备份恢复、主从复制;
redo log用于掉电等故障恢复。

(1) 如果不小心整个数据库的数据被删除了,能使用redo log文件恢复数据吗?

不可以使用redo log文件恢复,只能使用binlog文件恢复。

因为redo log文件是循环写,是会边写边擦除日志的,只记录未被刷入磁盘的数据的物理日志,已经刷入磁盘的数据都会从redo log文件里擦除。

binlog文件保存的是全量的日志,也就是保存了所有数据变更的情况,理论上只要记录在binlog上的数据,都可以恢复,所以如果不小心整个数据库的数据被删除了,得用binlog文件恢复数据。

(2) MySQL在完成一条更新操作后,Server.层会生成一条binlog,Bin Log也是采用WL模式,先写日志,再写磁盘。

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

因为一个事务的binlog?不能被拆开,无论这个事务多大,也要确保一次性写入,所以系统会给每个线程分配一块内存作为binlog cache。

至于什么时候刷新到磁盘,可以sync_binlog配置参数指定。

  • 0(延迟写)每次提交事务都不会刷盘,由系统自己决定什么时候刷盘,可能会丢失数据。
  • 1(实时写)每次提交事务,都会刷盘,性能较差。
  • N(延迟写)提交N个事务后,才会刷盘。

加入写Bin Log 后的事务流程

在这里插入图片描述

加入写Bin Log.之后的事务流程,先写处于prepare状态的Redo Log,事务提交后,再写处于commit状态的Redo Log,这就是二阶段提交的概念。

两阶段提交

redo log(重做日志)让 InnoDB 存储引擎拥有了崩溃恢复能力。
bin log(归档日志)保证了MySQL集群架构的数据一致性。

bin log是MySQL Server提供的一种日志,叫做归档日志,所有引擎都可以使用bin log。

redo log是 InnoDB 引擎特有的。
redo log主要记录的是某个数据页做了什么修改,bin log记录的是语句的原始逻辑,比如更新了某一行的某个字段。
redo log是循环写的,数据会被覆盖。bin log是追加写,一个文件写满,就写下一个文件。

两者是如何配合完成两阶段提交的。

在这里插入图片描述

从上图中可以看出,在最后提交事务的时候,有3个步骤:
1.写入redo log,处于prepare状态。
2.写bin log。
3.修改redo log状态变为commit。

先写处于prepare状态的Redo Log,事务提交后,再写处于commit状态的Redo Log。由于redo log的提交分为prepare和commit两个阶段,所以称之为两阶段提交。

为什么需要两阶段提交?

(1) redo log与bin log两份日志之间的逻辑不一致,会出现什么问题?

以updatei语句为例,假设id=2的记录,字段c值是0,把字段c值更新成1,SQL语句为update T set c=1 where id=2。假设执行过程中写完redo log日志后,binlog日志写期间发生了异常,会出现什么情况呢?由于binlog没写完就异常,这时候binlog里面没有对应的修改记录。因此,之后用binlog日志恢复数据时,就会少这一次更新,恢复出来的这一行c值是0,而原库因为redo log日志恢复,这一行c值是1,最终数据不一致。

为了解决两份日志之间的逻辑一致问题,InnoDB存储引擎使用两阶段提交方案。

(2)使用两阶段提交后,写入bin log时发生异常也不会有影响,因为MySQL根据redo log日志恢复数据时,发现redo log还处于prepare阶段,并且没有对应bin log日志,就会回滚该事务。

在这里插入图片描述

(3)再看一个场景,redo log设置commit阶段发生异常,那会不会回滚事务呢?

并不会回滚事务,虽然redo log是处于prepare 阶段,但是能通过事务id找到对应的bin log日志,所以MySQL认为是完整的,就会提交事务恢复数据。

在这里插入图片描述

MySQL主从复制

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

这个过程一般是异步的,也就是主库上执行事务操作的线程不会等待复制binlog的线程同步完成。

MySQL集群的主从复制过程梳理成3个阶段:

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

在这里插入图片描述

具体详细过程如下:

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

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

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

所以在实际使用中,一个主库一般跟2~3个从库(1套数据库,1主2从1备主),这就是一主多从的MySQL集群结构。


总结

1.二进制日志bin log (归档日志)事务日志redo log(重做日志)undo log(回滚日志)
2.bin log用于备份恢复、主从复制;redo log用于掉电等故障恢复;
3.redo log一旦提交意味着持久化了,但是有时候需要对其进行rollback操作,那就需要undo log;
4.主从:写数据时只写主库,在读数据时只读从库,这样即使写请求会锁表或者锁记录,也不会影响读请求的执行;

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐