MySQL实战 | 02-MySQL 如何恢复到半个月内任意一秒的状态? (2)

1、Master Thread 每秒一次执行刷新 Innodb_log_buffer 到重做日志文件;
2、每个事务提交时会将重做日志刷新到重做日志文件;
3、当 redo log 缓存可用空间少于一半时,重做日志缓存被刷新到重做日志文件;

有了 redo log,InnoDB 就可以保证即使数据库发生异常重启,之前提交的记录都不会丢失,这个能力称为 crash-safe。

CrashSafe 能够保证 MySQL 服务器宕机重启后:

所有已经提交的事务的数据仍然存在。

所有没有提交的事务的数据自动回滚。

binlog

如前文所讲,MySQL 整体可以分为 Server 层和引擎层。

其实,redo log 是属于引擎层的 InnoDB 所特有的日志,而 Server 层也有自己的日志,即 binlog(归档日志)。

记录了什么

逻辑格式的日志,可以简单认为就是执行过的事务中的 sql 语句。

但又不完全是 sql 语句这么简单,而是包括了执行的 sql 语句(增删改)反向的信息。

也就意味着 delete 对应着 delete 本身和其反向的 insert;update 对应着 update 执行前后的版本的信息;insert 对应着 delete 和 insert 本身的信息。

何时产生 & 释放

事务提交的时候,一次性将事务中的 sql 语句按照一定的格式记录到 binlog 中。因此,对于较大事务的提交,可能会变得比较慢一些。

binlog 的默认是保持时间由参数 expire_logs_days 配置,也就是说对于非活动的日志文件,在生成时间超过配置的天数之后,会被自动删除。

区别

1、redo log 是 InnoDB 引擎特有的,binlog 是 MySQL 的 Server 层实现,所有引擎都可以使用;
2、内容不同:redo log 是物理日志,记录的是在数据页上做了什么修改,是正在执行中的 dml 以及 ddl 语句;而 binlog 是逻辑日志,记录的是语句的原始逻辑,已经提交完毕之后的 dml 以及 ddl sql 语句,如「给 ID=2 的这一行的 c 字段加 1」;
3、写方式不同:redo log 是循环写的,空间固定;binlog 是可以一直追加写的,一个文件写到一定大小后,会继续写下一个,之前写的文件不会被覆盖;
4、作用不同:redo log 主要用来保证事务安全,作为异常 down 机或者介质故障后的数据恢复使用,binlog 主要用来做主从复制和即时点恢复时使用;
5、另外,两者日志产生的时间,可以释放的时间,在可释放的情况下清理机制,都是完全不同的。

参考:
[][6]

数据更新事务流程

有了对这两个日志的概念性理解,我们再来看执行器和 InnoDB 引擎在执行这个简单的 update 语句时的内部流程。

1、执行器先找引擎取 ID=2 这一行。ID 是主键,引擎直接用树搜索找到这一行。如果 ID=2 这一行所在的数据页本来就在内存中,就直接返回给执行器;否则,需要先从磁盘读入内存,然后再返回。

2、执行器拿到引擎给的行数据,把这个值加上 1,比如原来是 N,现在就是 N+1,得到新的一行数据,再调用引擎接口写入这行新数据。

3、引擎将这行新数据更新到内存中,同时将这个更新操作记录到 redo log 里面,此时 redo log 处于 prepare 状态。然后告知执行器执行完成了,随时可以提交事务。

4、执行器生成这个操作的 binlog,并把 binlog 写入磁盘

5、执行器调用引擎的提交事务接口,引擎把刚刚写入的 redo log 改成提交(commit)状态,更新完成。

[事务流程][7]

两阶段提交

上面处理 redo log 和 binlog 看着是不是有点懵逼?

其实这就是所谓的两阶段提交,即 COMMIT 会被自动的分成 prepare 和 commit 两个阶段。

[两阶段提交][8]

MySQL 在 prepare 阶段会生成 xid,然后会在 commit 阶段写入到 binlog 中。在进行恢复时事务要提交还是回滚,是由 Binlog 来决定的。

由上面的二阶段提交流程可以看出,通过两阶段提交方式保证了无论在任何情况下,事务要么同时存在于存储引擎和 binlog 中,要么两个里面都不存在。

这样就可以保证事务的 binlog 和 redo log 顺序一致性。一旦阶段 2 中持久化 Binlog 完成,就确保了事务的提交。

此外需要注意的是,每个阶段都需要进行一次 fsync 操作才能保证上下两层数据的一致性。

PS:记录 Binlog 是在 InnoDB 引擎 Prepare(即 Redo Log 写入磁盘)之后,这点至关重要。
另外需要注意的一点就是,SQL 语句产生的 Redo 日志会一直刷新到磁盘(master thread 每秒 fsync redo log),而 Binlog 是事务 commit 时才刷新到磁盘,如果 binlog 太大则 commit 时会慢。

参考:
[?p=7892][9]

如何恢复数据?

当需要恢复到指定的某一秒时,比如某天下午两点发现中午十二点有一次误删表,需要找回数据,那你可以这么做:

1、首先,找到最近的一次全量备份,如果你运气好,可能就是昨天晚上的一个备份,从这个备份恢复到临时库;

2、然后,从备份的时间点开始,将备份的 binlog 依次取出来,重放到中午误删表之前的那个时刻。

这样你的临时库就跟误删之前的线上库一样了,然后你可以把表数据从临时库取出来,按需要恢复到线上库去。

当遇到 crash 时,恢复的过程也非常简单:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wssxfw.html