Hello,Hi,你好,我是猿java。
工作或者面试中,经常会遇到 MySQL 数据库 binlog、undo log、redo log 相关的知识点,今天我们就来一起深入分析这三种 log。
申明:本文基于 MySQL 8.0.30,默认为 InnoDB 引擎;InnoDB 由 Innobase Oy公司所开发,2006年五月时由甲骨文公司并购。
前言 {#前言}
在正式进入主题之前,我们先看一张 MySQL的架构示意图:
在上述 MySQL架构图中是不是看到了红色字体:binlog、undo log、redo log?
没错,这就是 MySQL数据库里三种 log日志的分布图,我们再看一张日志在磁盘上的存储图(此处是Mac os安装 MySQL):
binlog {#binlog}
binlog,是 binary log的英文缩写,翻译为二进制日志或者归档日志(带有业务含义),它是从 MySQL 3.23.14版本引入的。binlog是在 MySQL Server层实现,因此所有引擎都可以使用它。
binlog包含的信息 {#binlog包含的信息}
- MySQL数据库所有的表结构变更以及表数据修改的二进制日志(像 select,show这种查询类的操作,不会记录);
- 每条语句使用更新数据多长时间的信息;
binlog日志的三个用途 {#binlog日志的三个用途}
- 归档日志
- 主从复制
- 数据恢复
binlog日志的三种类型 {#binlog日志的三种类型}
- Statement-based logging:语句模式,包含产生数据更改(插入、更新、删除)的 SQL语句;
- Row-based logging:行模式,用于记录单个行的更改,从 MySQL 5.1版本引入;
- Mixed logging:混合模式,默认使用语句模式,可以按需自动切换到行模式,从 MySQL 5.1版本引入;
接下来,我们来查看下 binlog日志到底长啥样:
我们先创建一张user表,然后对user表进行增删改查操作,具体操作截图如下:
查看上述操作的 binlog日志信息,查看指令和结果截图如下:
|---------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| # 查看 binlog是否开启,8.0.30 默认是开启的 mysql> show variables like 'log_bin'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | log_bin | ON | +---------------+-------+ 1 row in set (0.02 sec) # 查看所有的binlog日志文件 mysql> show binary logs; # 查看某个 binlog的具体信息,通过指令也可以看出binlog是以 event的方式存储 mysql> show binlog events in 'binlog.000001'
|
查看上面的 binlog日志我们可以看出,在 binlog中,并没有把我们执行的 SQL语句直接存储,而是转换成了一些逻辑步骤,所以, binlog它是一种逻辑日志。
undo log {#undo-log}
undo log: 翻译成撤销日志或回滚日志,用于事务回滚,保证了事务 ACID 特性中的原子性(Atomicity),同时还可以配合 ReadView 实现多版本控制(MVCC)。
undo log 相关参数 {#undo-log-相关参数}
可以通过 show variables like '%undo%'; 指令查看 undo log相关参数:
|---------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11
| mysql> show variables like '%undo%'; +--------------------------+------------+ | Variable_name | Value | +--------------------------+------------+ | innodb_max_undo_log_size | 1073741824 | | innodb_undo_directory | ./ | | innodb_undo_log_encrypt | OFF | | innodb_undo_log_truncate | ON | | innodb_undo_tablespaces | 2 | +--------------------------+------------+ 5 rows in set (0.01 sec)
|
- innodb_max_undo_log_size:一个 undo log文件对应的最大值,默认 1G;
- innodb_undo_directory:undo log文件存放的目录;
- innodb_undo_log_encrypt:是否对 undo log文件开启空间压缩,默认是关闭;
- innodb_undo_log_truncate:单个文件超过最大值时,是否对 undo log文件进行切分,默认为打开状态;
- innodb_undo_tablespaces:单个文件超过最大值时,undo log文件会被切分为几份,默认是 2;
事务回滚 {#事务回滚}
在事务提交之前,MySQL 会将更新前的数据记录到 undo log 日志文件里,当事务回滚时,可以利用 undo log 来进行回滚。如下图:
每当 InnoDB 引擎执行一条更新操作(修改、删除、新增)时,就会生成对应的一条回滚指令记录在 undo log 里,比如:
- InnoDB 引擎执行 insert 操作,则会在 undo log 日志里面保存一条相反的 delete 语句;比如: insert into t(id, name) values(1,'zhangsan'); 则 undo log 对应的回滚日志为 delete from t where id = 1;
- InnoDB 引擎执行 delete 操作,则会在 undo log 日志里面保存一条相反的 insert 语句;比如: delete from t where id = 1; 则 undo log 对应的回滚日志为 insert into t(id, name) values(1,'zhangsan');
- InnoDB 引擎执行 update 操作,则会在 undo log 日志里面保存一条相反的 update 语句;比如: update t set name = 'lisi' where id = 1; 则 undo log 对应的回滚日志为 update t set name = 'zhangsan' where id = 1;
undo log 和 ReadView 实现多版本控制(MVCC) {#undo-log-和-ReadView-实现多版本控制(MVCC)}
在 InnoDB引擎中,可以多个事务对同一条数据记录进行更新操作,当出现异常时,能及时进行数据回滚,那么 InnoDB是如何能精确的把数据回滚到具体的哪一个版本呢?这就是 InnoDB的多版本控制机制。
如下图:有 3个事务分别对表中id = 1行记录进行更新操作,因此,在undo log文件中就会产生3条逻辑回滚日志
redo log {#redo-log}
redo log,翻译成重做日志,用于crash-safe,即当数据库发生异常重启,可以保证之前提交的记录不会丢失,它是 InnoDB引擎独有的日志。
常见问题 {#常见问题}
为什么需要 binlog,redo log两份log {#为什么需要-binlog,redo-log两份log}
MySQL 如何辨别 binlog 的完整性? {#MySQL-如何辨别-binlog-的完整性}
- statement 格式的 binlog,文件末尾有 COMMIT;
- row 格式的 binlog,文件末尾有一个 XID event。
redo log 和 binlog 是怎么关联起来的? {#redo-log-和-binlog-是怎么关联起来的}
它们有一个共同的数据字段,叫 XID。崩溃恢复的时候,会按顺序扫描 redo log:如果碰到既有 prepare、又有 commit 的 redo log,就直接提交;如果碰到只有 parepare、而没有 commit 的 redo log,就拿着 XID 去 binlog 找对应的事务。
处于 prepare 阶段的 redo log 加上完整 binlog,重启就能恢复,MySQL 为什么要这么设计? {#处于-prepare-阶段的-redo-log-加上完整-binlog,重启就能恢复,MySQL-为什么要这么设计}
其实,这个问题还是跟我们在反证法中说到的数据与备份的一致性有关。在时刻 B,也就是 binlog 写完以后 MySQL 发生崩溃,这时候 binlog 已经写入了,之后就会被从库(或者用这个 binlog 恢复出来的库)使用。所以,在主库上也要提交这个事务。采用这个策略,主库和备库的数据就保证了一致性。
为什么需要两阶段提交呢? {#为什么需要两阶段提交呢?}
两阶段提交是经典的分布式系统问题,并不是 MySQL 独有的。如果必须要举一个场景,来说明这么做的必要性的话,那就是事务的持久性问题。对于 InnoDB 引擎来说,如果 redo log 提交完成了,事务就不能回滚(如果这还允许回滚,就可能覆盖掉别的事务的更新)。而如果 redo log 直接提交,然后 binlog 写入的时候失败,InnoDB 又回滚不了,数据和 binlog 日志又不一致了。两阶段提交就是为了给所有人一个机会,当每个人都说"我 ok"的时候,再一起提交。
总结 {#总结}
- undo log(回滚日志):是 Innodb 存储引擎层的逻辑日志,实现了事务中的原子性,主要用于事务回滚和 MVCC。
- redo log(重做日志):是 Innodb 存储引擎层的物理日志,是循环写,实现了事务中的持久性,主要用于掉电等故障恢复;
- binlog (归档日志):是 Server 层生成的日志,所有引擎都可使用,主要用于数据备份、数据恢复和主从复制;