MySQL的binlog、redo log和undo log怎么使用

62次阅读
没有评论

共计 6691 个字符,预计需要花费 17 分钟才能阅读完成。

这篇文章主要介绍“MySQL 的 binlog、redo log 和 undo log 怎么使用”,在日常操作中,相信很多人在 MySQL 的 binlog、redo log 和 undo log 怎么使用问题上存在疑惑,丸趣 TV 小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”MySQL 的 binlog、redo log 和 undo log 怎么使用”的疑惑有所帮助!接下来,请跟着丸趣 TV 小编一起来学习吧!

1、binlog

binlog 用于记录数据库执行的写入性操作 (不包括查询) 信息,以二进制的形式保存在磁盘中。binlog 是 mysql 的逻辑日志,并且由 Server 层进行记录,使用任何存储引擎的 mysql 数据库都会记录 binlog 日志。

逻辑日志:可以简单得理解为 sql 语句;

物理日志:MySQL 中数据都是保存在数据页中的,物理日志记录的是数据页上的变更;在这里插入代码片

binlog 是通过追加的方式进行写入的,可以通过 max_binlog_size 参数设置每个 binlog 文件的大小,当文件大小达到给定值之后,会生成新的文件来保存日志。

binlog 使用场景
项目 在实际应用中,binlog 的主要使用场景有两个,分别是主从复制和数据恢复。

主从复制:在 Master 端开启 binlog,然后将 binlog 发送到各个 Slave 端,Slave 端重放 binlog 从而达到主从数据一致。

数据恢复:通过使用 mysqlbinlog 工具来恢复数据。

MySQL 主从同步原理

主节点 binlog dump 线程
当从节点连接主节点时,主节点会创建一个 log dump 线程,用于发送 binlog 的内容。在读取 binlog 中的操作时,此线程会对主节点上的 binlog 加锁,当读取完成,甚至在发动给从节点之前,锁会被释放;

从节点 I / O 线程
当从节点上执行 start slave 命令之后,从节点会创建一个 I / O 线程用来连接主节点,请求主库中更新的 binlog。I/ O 线程接收到主节点 binlog dump 进程发来的更新之后,保存在本地 relaylog 中;

从节点 SQL 线程
SQL 线程负责读取 relaylog 中的内容,解析成具体的操作并执行,最终保证主从数据的一致性;
MySQL 数据库主从同步原理

binlog 的内容
上面说了,binlog 是一种逻辑日志,可以简单得理解为 sql 语句,但是实际上还包含着执行的 sql 语句的反向逻辑。delete 对应着 delete 本身以及反向的 insert 信息;update 包含着对应的 update 执行前后数据行的相关信息;insert 包含自身的 insert 以及对应的 delete 信息。

binlog 的格式
binlog 共有三种格式,分别是 statement、row 以及 mixed。MySQL 5.7.7 版本之前默认使用的是 statement,MySQL 5.7.7 之后默认使用的是 row。日志的格式可以通过 my.ini 配置文件中的 binlog-format 来修改。
(1)statement:基于 sql 语句的复制(statement-based replication,SBR),每一条修改数据的 sql 语句都会记录到 binlog 中。

优点:不需要具体记录某一行的变化,节约空间,减少 io,提高性能;

缺点:在执行 sysdate()或者 sleep()等操作的时候,可能导致主从数据不一致的情况;

(2)row:基于行记录的复制(row-based replication,RBR),不记录 sql 语句上下文相关信息,而是记录哪条记录被修改的细节。

优点:非常详细地记录每一行记录修改的细节,因而不会出现数据无法被正确复制的情况;

缺点:由于会非常详细地记录每一条记录修改的细节,这样会产生大量的日志内容。假设现在有一条 update 语句,修改了很多条记录,则每条修改记录都会记录到 binlog 中。特别地,alter table 这个操作,由于表结构的变化,每行记录都会发生变化,导致日志量暴增;

(3)mixed:根据上面所说的,statement 和 row 各有优缺点,因此出现了 mixed 这个版本,将这二者进行混合。一般情况下使用 statement 格式来进行保存,当遇到 statement 无法解决时,切换为 row 格式来进行保存。
特别地,上面说了,新版本(MySQL 5.7.7 之后)默认使用的 row 格式,这里的 row 也做了相应的优化,在遇到 alter table 这个操作时采用 statement 格式进行记录,其余操作仍然使用 row 格式。

binlog 刷盘时机

对于 InnoDB 存储引擎来说,只有在事务提交的时候才会记录 binlog,此时记录还在内存中,MySQL 通过 sync_binlog 来控制 binlog 的刷盘时机,取值范围为 0 -N:

0:不强制刷到磁盘,由系统自行判断何时写入磁盘中;

1:每次提交后都要将 binlog 写入磁盘中;

N:每 N 个事务,才会将 binlog 写入磁盘中;

从上面可以看出,sync_binlog 最安全的是设置是 1,这也是 MySQL 5.7.7 之后版本的默认值。但是设置一个大一些的值可以提升数据库性能,因此实际情况下也可以将值适当调大,牺牲一定的一致性来获取更好的性能。

binlog 的物理文件大小

在 my.ini 配置文件中,可以通过 max_binlog_size 来配置 binlog 的大小。当日志量超过 binlog 文件的大小时,系统会重新生成一个新的文件来继续保存文件。当一个事务比较大时,或者是当日志越来越多的时候,此时占据的物理空间太大怎么办?MySQL 提供了一种自动删除的机制,还是在 my.ini 配置文件中,可以通过配置 expire_logs_days 这个参数来解决,单位为天。当这个参数为 0,表示永不删除;为 N 时,表示第 N 天后自动删除。

2、redo log

redolog 是 InnoDB 引擎专有的日志系统。主要是用来实现事务的持久性以及实现 crash-safe 功能。redolog 属于物理日志,记录的是 sql 语句执行之后数据页上的具体修改内容。
我们都知道,当 MySQL 运行的时候,会将数据从磁盘中加载到内存当中。当执行 sql 语句对数据进行修改时,修改后的内容其实都只是暂时保存到内存当中,如果此时断电或者出现其他情况,这些修改就会丢失。因而,当修改完数据之后,MySQL 会寻找机会将这些内存中的记录刷回到磁盘当中。但这就出现一个性能问题,主要有两个方面:

InnoDB 中是以页为数据单位与磁盘进行交互的,而一个事务很可能只是修改了一个页上的几个字节,如果将一个完整的数据页刷回磁盘当中,浪费资源;

一个事务可能涉及到多个数据页,这些数据页只是逻辑上连续,在物理上并不连续,使用随机 IO 性能太差;

因此,MySQL 设计了 redolog 来记录事务对数据页具体做了哪些修改,之后将 redolog 再刷回磁盘当中。你可能会有疑惑,本来就是想减少 io,这不又加上一次 io 么?InnoDB 的设计者在设计之初就已经考虑到了这些。redolog 文件一般都比较小,且在刷回磁盘的过程中是顺序 io,相比于随机 io 来说,性能更好。

redo log 基本概念
redolog 由两部分组成,一个是内存中的日志缓存 redo log buffer,一个是磁盘中的日志文件 redo log file。当每次对数据记录进行修改的时候,都会将这些修改内容先写入 redo log buffer 中,后续等待合适的时机将内存中的修改刷回到 redo log file 中。这种先写日志,再写磁盘的技术就是 WAL(Write-Ahead Logging) 技术。需要注意的是 redolog 比数据页先刷回磁盘,聚簇索引,二级索引,undo 页面的修改,均需要记录 redolog。

在计算机操作系统中,用户空间 (user space) 下的缓冲区数据一般情况下是无法直接写入磁盘的,中间必须经过操作系统内核空间 (kernel space) 缓冲区 (OS Buffer)。因此,redo log buffer 写入 redo log file 实际上是先写入 OS Buffer,然后再通过系统调用 fsync() 将其刷到 redo log file 中,过程如下:

mysql 支持三种将 redo log buffer 写入 redo log file 的时机,可以通过 innodb_flush_log_at_trx_commit 参数配置,各参数值含义如下:

参数值含义 0(延迟写)事务提交时不会将 redo log buffer 中日志写入到 os buffer,而是每秒写入 os buffer 并调用 fsync()写入到 redo log file 中。也就是说设置为 0 时是 (大约) 每秒刷新写入到磁盘中的,当系统崩溃,会丢失 1 秒钟的数据。1(实时写,实时刷)事务每次提交都会将 redo log buffer 中的日志写入 os buffer 并调用 fsync()刷到 redo log file 中。这种方式即使系统崩溃也不会丢失任何数据,但是因为每次提交都写入磁盘,IO 的性能较差。2(实时写,延迟刷)每次提交都仅写入到 os buffer,然后是每秒调用 fsync()将 os buffer 中的日志写入到 redo log file。

redo log 记录形式
redolog 采用固定大小,循环写入的格式,当 redolog 写满之后,会重新从头开始写。为什么这么设计呢?
redo log 存在的意义主要就是降低对数据页刷盘的要求。redolog 记录了数据页上的修改,但是当数据页也刷回到磁盘后,这些记录就失去作用了。因此当 MySQL 判断之前的 redolog 已经失去作用之后,新数据会将这些失效的数据进行覆盖。那如何判断该不该进行覆盖呢?

上图是 redo log file 的示意图,write pos 表示 redolog 当前记录的日志序列号 LSN(log sequence number)。当数据页也已经刷回磁盘之后,会更新 redo log file 中的 LSN,表示到这个 LSN 之前的数据已经落盘,这个 LSN 就是 check point。write pos 到 check point 之间的部分是 redolog 空余的部分,用于记录新的记录;check point 到 write pos 之间是 redolog 已经记录的数据页修改部分,但此时数据页还未刷回磁盘的部分。当 write pos 追上 check point 时,会先推动 check point 向前移动,空出位置再记录新的日志。

启动 innodb 的时候,不管上次是正常关闭还是异常关闭,总是会进行恢复操作。恢复时,会先检查数据页中的 LSN,如果这个 LSN 小于 redolog 中的 LSN,即 write pos 位置,说明在 redolog 上记录着数据页上尚未完成的操作,接着就会从最近的一个 check point 出发,开始同步数据。

那有没有可能数据页中的 LSN 大于 redolog 中的 LSN 呢?答案是当然可能。出现这种情况时,这时超出 redolog 的部分将不会重做,因为这本身就表示已经做过的事情,无需再重做。
redo log 与 binlog 区别

redo logbinlog 文件大小 redo log 的大小是固定的。binlog 可通过配置参数 max_binlog_size 设置每个 binlog 文件的大小。实现方式 redo log 是 InnoDB 引擎层实现的,并不是所有引擎都有。binlog 是 Server 层实现的,所有引擎都可以使用 binlog 日志记录方式 redo log 采用循环写的方式记录,当写到结尾时,会回到开头循环写日志。binlog 通过追加的方式记录,当文件大小大于给定值后,后续的日志会记录到新的文件上适用场景 redo log 适用于崩溃恢复(crash-safe)binlog 适用于主从复制和数据恢复

由 binlog 和 redo log 的区别可知:binlog 日志只用于归档,只依靠 binlog 是没有 crash-safe 能力的。但只有 redo log 也不行,因为 redo log 是 InnoDB 特有的,且日志上的记录落盘后会被覆盖掉。因此需要 binlog 和 redo log 二者同时记录,才能保证当数据库发生宕机重启时,数据不会丢失。
两阶段提交
上面简单介绍了 redolog 和 binlog,在对数据进行修改时,他们都会对这些修改进行保存落地,只是一个是物理日志,一个是逻辑日志。那他俩具体在修改过程中是如何执行的呢?

假设现在有一条 update 语句要执行,update from table_name set c=c+1 where id=2,执行流程如下:

先定位到 id= 2 这一条记录;

执行器拿到引擎给的行数据,把这个值加上 1,得到新的一行数据,再调用引擎接口写入这行新数据;

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

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

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

示意图如下所示:

这种将 redolog 的写入拆分成 prepare 和 commit 两个步骤的过程称之为两阶段提交。

redolog 和 binlog 都可以用于表示事务的提交状态,而两阶段提交就是让这两个状态保持逻辑上的一致。如果不使用两阶段提交,而是先写其中一个再写另外一个可能会带来一些问题。

此时还是使用 update 来举例。假设当前 id=2,有一个字段 c =0,分别分析以下情况:
先写 redolog 再写 binlog
假设先写 redolog,当 redolog 写完,但是 binlog 还未写完的时候,此时 MySQL 突然出现异常导致重启。由于之前 redolog 已经写完,系统重启后,修改的记录仍然存在,所以恢复后这一行 c 的值是 1。但由于系统重启,binlog 中并未有这条记录。之后备份日志的时候,存起来的 binlog 里面就没有这条语句。然后你会发现,如果需要用这个 binlog 来恢复临时库的话,由于这个语句的 binlog 丢失,这个临时库就会少了这一次更新,恢复出来的这一行 c 的值就是 0,与原库的值不同。
先写 binlog 再写 redolog
假如先写 binlog,然后写 redolog 的时候系统重启。重启之后,redolog 中没有对 c 进行修改的记录,此时 c 的值还是 0。但是 binlog 里面已经记录了“把 c 从 0 改成 1”这个日志。所以,在之后用 binlog 来恢复的时候就多了一个事务出来,恢复出来的这一行 c 的值就是 1,与原库的值不同。

因此,综上所述,如果是先写某一个日志再写另一个日志,就会出现数据库的状态与使用 binlog 恢复出来的库的状态不一致的情况。

3、undo log

undolog 主要用来记录某条行记录被修改之前的状态,记录的是修改前的数据。这样的话,当事务进行回滚时,就可以通过 undolog 将记录恢复到事务开始前的样子。事务的原子性和持久性也是依靠 undolog 来实现的。undo log 主要记录了数据的逻辑变化,比如一条 INSERT 语句,对应一条 DELETE 的 undo log,对于每个 UPDATE 语句,对应一条相反的 UPDATE 的 undo log,这样在发生错误时,就能回滚到事务之前的数据状态。同时,在进行数据恢复的时候,与 binlog,redolog 结合使用,保证了数据恢复的正确性。

undolog 的作用流程如下所示:

在事务开始之前将修改前的版本写入到 undo log 中;

开始进行修改,将修改过的数据保存到内存当中;

将 undolog 持久化到磁盘当中;

将数据页刷回到磁盘当中;

事务提交;

需要注意的是,与 redolog 一样,undolog 也是要先于数据页刷回到磁盘当中。在恢复数据时,如果 undolog 是完整的,可以根据 undolog 来回滚事务。

在一个事务当中,可能会对同一条数据进行多次修改,那么是不是每一次修改前的记录都要记录到 undolog 中呢?这样的话,会导致 undolog 日志量太大,此时 redolog 就要上场了。在一个事务当中,如果是对同一条记录进行修改,undolog 只会记录事务开始前的原始记录,当再次对这条记录进行修改时,redolog 会记录后续的变化。在数据恢复时,redolog 完成前滚,undolog 完成回滚,二者相互协调完成数据的恢复。过程如下所示:

到此,关于“MySQL 的 binlog、redo log 和 undo log 怎么使用”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注丸趣 TV 网站,丸趣 TV 小编会继续努力为大家带来更多实用的文章!

正文完
 
丸趣
版权声明:本站原创文章,由 丸趣 2023-07-15发表,共计6691字。
转载说明:除特殊说明外本站除技术相关以外文章皆由网络搜集发布,转载请注明出处。
评论(没有评论)