MySQL日志详解——日志分类、二进制日志bin log、回滚日志undo log、重做日志redo log

news/2025/2/6 21:39:53/文章来源:https://www.cnblogs.com/qhhfRA/p/18701731

文章目录

一、前言

  • 1.1 MySQL体系结构

  • 1.2 MySQL日志分类

  • 1.3 其他几种日志

    • 1.3.1 查询日志
    • 1.3.2 慢查询日志
    • 1.3.3 错误日志

二、bin log 二进制日志

  • 2.1 bin log简介
  • 2.2 binlog日志格式
  • 2.3 日志删除
  • 2.4 写入/刷盘机制

三、undo log 回滚日志

  • 3.1 undo log简介
  • 3.2 隐藏字段 —— 事务ID(TRX_ID)、ROLL_PTR
  • 3.3 版本链

四、redo log 重做日志

  • 4.1 redo log详解
  • 4.2 redo log的写入过程、刷盘时机
  • 4.3 redo log file 的结构
  • 4.4 什么是 crash-save
  • 4.5 redo log细节

五、补充

  • 5.1 总结、使用场景

    • binlog的应用场景
    • undolog的使用场景
    • redolog的使用场景
  • 5.2 binlog与redolog对比、逻辑日志与物理日志

  • 5.3 update语句的执行流程

  • 5.4 两阶段提交

  • 5.5 MySQL主从复制

    • 5.5.1 作用
    • 5.5.2 原理
    • 5.5.3 同步数据一致性

MySQL作为最流行的开源数据库,其重要性不言而喻。日志是mysql数据库的重要组成部分,记录着数据库运行期间各种状态信息。常见的日志有以下几种:

作为开发,我们重点需要关注的是 二进制日志bin log(归档日志)事务日志redo log(重做日志)undo log(回滚日志),本文接下来会详细介绍这三种日志。

  • 二进制binlog(归档日志):是Server层生成的日志,主要用于数据备份和主从复制;
  • undolog(回滚日志):是Innodb存储引擎层生成的日志,实现了事务中的原子性,主要用于事务回滚和MVCC
  • 事务日志redolog(重做日志):是Innodb存储引擎层生成的日志,实现了事务中的持久性,主要用于掉电等故障恢复

下面就带着这个问题,看看这三种日志是怎么工作的。

一、前言

1.1 MySQL体系结构


1)连接层

最上层是一些客户端和链接服务,包含本地sock 通信和大多数基于客户端/服务端工具实现的类似于TCP/IP的通信。主要完成一些类似于连接处理、授权认证、及相关的安全方案。在该层上引入了线程池的概念,为通过认证安全接入的客户端提供线程。同样在该层上可以实现基于SSL的安全链接。服务器也会为安全接入的每个客户端验证它所具有的操作权限。

2)服务层

第二层架构主要完成大多数的核心服务功能,如SQL接口,并完成缓存的查询,SQL的分析和优化,部分内置函数的执行。所有跨存储引擎的功能也在这一层实现,如过程、函数等。在该层,服务器会解析查询并创建相应的内部解析树,并对其完成相应的优化如确定表的查询的顺序,是否利用索引等,最后生成相应的执行操作。如果是select语句,服务器还会查询内部的缓存,如果缓存空间足够大,这样在解决大量读操作的环境中能够很好的提升系统的性能。

3)引擎层

存储引擎层,存储引擎真正的负责了MySQL中数据的存储和提取,服务器通过API和存储引擎进行通信。不同的存储引擎具有不同的功能,这样我们可以根据自己的需要,来选取合适的存储引擎。数据库中的索引是在存储引擎层实现的。

4)存储层

数据存储层,主要是将数据(如: redolog、undolog、数据、索引、二进制日志、错误日志、查询日志、慢查询日志等)存储在文件系统之上,并完成与存储引擎的交互。

和其他数据库相比,MySQL有点与众不同,它的架构可以在多种不同场景中应用并发挥良好作用。主要体现在存储引擎上,插件式的存储引擎架构,将查询处理和其他的系统任务以及数据的存储提取分离。这种架构可以根据业务的需求和实际需要选择合适的存储引擎。

其中slowlog、binlog、errorlog、relaylog归属于MySQL服务层;undolog、redolog归属于引擎层,为innodb所特有。

1.2 MySQL日志分类

MySQL有不同类型的日志:慢查询日志、通用查询日志、错误日志、事务日志、二进制日志等几大类,在MySQL8之后又新增了两种日志——中继日志、数据定义语句日志。其中比较重要的是二进制日志binlog (归档日志) 、事务日志redo log(重做日志) 和 undo log(回滚日志)。

MySQL日志主要包括八种

  • 慢查询日志(slow query log):记录所有执行时间超过long_query_time的所有查询,方便对查询进行优化
  • 通用查询日志(general log):记录索引连接的起始时间和终止时间,以及连接发送给数据库服务的所有指令,对复原操作的实际场景、发现问题、数据库操作的审计都有帮助
  • 二进制日志(bin log):记录所有更改数据的语句,用于主从服务器之间的数据同步、服务器遇到故障时数据的无损恢复
  • 错误日志(error log):记录MySQL服务的启动、运行或停止MySQL服务时出现的问题,方便了解服务器的状态,从而对服务器进行维护
  • 中继日志(relay log):用于主从服务器架构,从服务器用来存放主服务器二进制日志内容的一个中间件文件。从服务器通过读取中继日志的内容,来同步主服务器上的操作
  • 回滚日志(undo log):是Innodb存储引擎层生成的日志,实现了事务中的原子性,主要用于事务回滚和MVCC
  • 重做日志(redo log):是Innodb存储引擎层生成的日志,实现了事务中的持久性,主要用于掉电等故障恢复
  • 数据定义语句日志:记录数据定义语句执行的元数据操作

除了二进制日志外,其他日志均为文本文件。默认情况下,所有日志均创建于MySQL数据目录中

1.3 其他几种日志

1.3.1 查询日志

查询日志中记录了客户端的所有操作语句(包括所有的增删改查、DDL、DML、DQL语句),而二进制日志不包含查询数据的SQL语句。默认情况下,查询日志是未开启的。如果需要开启查询日志,可以设置以下配置:

如果想要禁用查询日志,可将general_log设置为0,而后重启MySQL服务sudo systemctl restart mysql

1.3.2 慢查询日志

慢查询日志记录了所有执行时间超过参数 long_query_time 设置值并且扫描记录数不小于 min_examined_row_limit 的所有SQL语句的日志,默认未开启。long_query_time 默认为10秒,最小为0,精度可以到微秒。如果要开启慢查询日志,需要在MySQL的配置文件(/etc/my.cnf)中配置如下信息:

# 开启MySQL慢日志查询开关
slow_query_log=1
# 执行时间参数,设置慢日志的时间为2秒,SQL语句执行时间超过2秒,就会视为慢查询,记录慢查询日志
long_query_time=2


同理,如果想要禁用慢查询日志,可将slow_query_log设置为0,而后重启MySQL服务sudo systemctl restart mysql

1.3.3 错误日志

错误日志是MySQL中最重要的日志之一,它记录了当mysqld启动和停止时,以及服务器在运行过程中发生任何严重错误时的相关信息。当数据库出现任何故障导致无法正常使用时,建议首先查看此日志。

该日志是默认开启的,默认存放目录/var/log/,默认的日志文件名为mysqld.log。查看日志位置:

-- 登录mysql,查看系统变量
show variables like '%log_error';

二、bin log 二进制日志

2.1 bin log简介

二进制日志(BINLOG)记录了所有的DDL(数据定义语言,创建库、表)语句和DML(数据操纵语言,增删改)语句,但不包括数据查询(SELECT、SHOW)语句

作用:

  1. 灾难时的数据恢复(通过使用 mysqlbinlog 工具来恢复数据);
  2. MySQL的主从复制(在Master端开启binlog ,每个从库读取binlog、写到暂存日志relay log中,slave端重放binlog 从而达到主从数据一致)。

细节:

  • binlog用于记录数据库执行的DDL、DML操作信息(不包括查询),以二进制的形式保存在磁盘中。binlog是mysql的逻辑日志,并且由Server层进行记录,使用任何存储引擎的mysql数据库都会记录binlog日志。
  • binlog是通过追加的方式进行写入的,可以通过max_binlog_size参数设置每个binlog文件的大小,当文件大小达到给定值之后,会生成新的文件来保存日志。
  • MySQL在完成一条更新操作后,Server层会生成一条binlog,binlog采用WAL模式(Write-Ahead Logging,redo log也是采用WAL模式),先写日志,再写磁盘:事务执行过程中,先把日志写入binlog cache,事务提交时,再把binlog cache写到binlog文件中

在MySQL8版本中,默认二进制日志是开启着的,涉及到的参数如下:

show variables like '%log_bin';

常用的5.7版本可能只有以下参数:

mysql> SELECT VERSION();
+-----------+
| VERSION() |
+-----------+
| 5.7.35    |
+-----------+
1 row in set (0.00 sec)mysql> show variables like '%log_bin';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| log_bin       | OFF   |
| sql_log_bin   | ON    |
+---------------+-------+
2 rows in set (0.00 sec)

2.2 binlog日志格式

binlog日志有三种格式:分别为STATMENT、ROW和MIXED,具体格式及特点如下:

  • STATMENT:基于SQL语句的复制,每一条会修改数据的sql语句会记录到binlog中。
    • 优点:不需要记录每一行的变化,减少了binlog日志量,节约了IO,从而提高了性能;
    • 缺点:在某些情况下会导致主从数据不一致,比如执行sysdate()、sleep()等。
  • ROW:基于行的复制,不记录每条sql语句的上下文信息,仅需记录哪条数据被修改了。
    • 优点:不会出现某些特定情况下的存储过程、或function、或trigger的调用和触发无法被正确复制的问题;
    • 缺点:会产生大量的日志,尤其是alter table的时候会让日志暴涨
  • MIXED:基于 STATMENT 和 ROW 两种模式的混合复制,一般的复制使用STATEMENT模式保存binlog,对于STATEMENT模式无法复制的操作使用ROW模式保存binlog
日志格式 含义
STATEMENT 基于SQL语句的日志记录,记录的是SQL语句,对数据进行修改的SQL都会记录在日志文件中
ROW 默认格式,基于行的日志记录,记录是每一行的数据变更
MIXED 混合了STATEMENT和ROW两种格式,默认采用STATEMENT,在某些特殊情况下会自动切换为ROW进行记录
show variables like '%binlog_format%';-- 如果想修改二进制日志格式
--  1.vim /etc/my.cnf
--  2.往文件内添加 binlog_format=STATEMENT
--  3.systemctl restart mysqld

具体日志格式详解

由于日志是以二进制方式存储的,不能直接读取,需要通过二进制日志查询工具mysqlbinlog来查看,具体语法:

mysqlbinlog [options] log-files参数选项:-d, --database=name     指定数据库名称,只列出指定的数据库相关操作-o, --offset=#          忽略掉日志中的前n行命令-v, --verbose           将行事件(数据变更)重构为SQL语句-vv                     将行事件(数据变更)重构为SQL语句,并输出注释信息mysqlbinlog -v binlog.00002

mysql中有score记录成绩,执行 update score set math = math + 1;

ROW格式下,执行mysqlbinlog -v binlog.00002,得到如下内容

STATEMENT格式下,执行mysqlbinlog binlog.00003,得到如下内容

2.3 日志删除

对于比较繁忙的业务系统,每天生成的binlog数据巨大,如果长时间不清除,将会占用大量磁盘空间。可以通过以下几种方式清理日志:

MySQL指令 含义
reset master; 删除全部binlog日志,删除之后,日志编号将从binlog.000001重新开始
purge master logs to 'binlog.******'; 删除******编号之前的所有日志
purge master logs before 'yyyy-mm-dd hh24:mi:ss'; 删除日志为“yyyy-mm-ddhh24:mi:ss"之前产生的所有日志

2.4 写入/刷盘机制

事务执行过程中,先把日志写入binlog cache,事务提交时,再把binlog cache写到binlog文件中。一个事务的binlog不能被拆开,确保一次性写入,系统将给每个线程分配一块内存作为binlog cache

对于InnoDB存储引擎而言,只有在事务提交时才会记录binlog,那么binlog什么时候才会将内存中的数据刷到磁盘呢?其实mysql是通过sync_binlog参数控制binlog的刷盘时机,取值范围是0-N【write和fsync的时机,由参数sync_binlog控制,默认为0】

  • 0:每次提交事务都只write,由系统自行判断什么时候执行fsync。虽然性能得到提升,但是机器宕机,page cache里的binlog会丢失(不去强制要求,由系统自行判断何时写入磁盘)
  • 1:每次提交事务都会执行fsync,如同redolog刷盘流程一样(每次commit的时候都要将binlog写入磁盘)
  • N:每次提交事务都write,但累计N个事务后才fsync(每N个事务,才会将binlog写入磁盘)

在出现IO瓶颈时,将sync_binlog设置成一个较大的值,能提升性能。同样的,若机器宕机会丢失最近N个事务的binlog日志

三、undo log 回滚日志

3.1 undo log简介

undo log(回滚日志):是Innodb存储引擎层生成的日志,实现了事务中的原子性,主要用于事务回滚和MVCC

它是InnoDB存储引擎在insert、update、delete的时候产生的便于数据回滚的日志。在数据更新之前,MySQL就需要先把更新前的数据记录到 undo log 日志中,当事务回滚时,可以利用 undo log 来进行回滚。作用包含两个——提供回滚、MVCC(多版本并发控制)。undo log主要分为两种:

  • insert undo log:当insert的时候,产生的undo log日志只在回滚时需要,在事务提交后,可被立即删除(因为这种log只是对本事务可见,其他事务不可见,所以当事务提交后,这种类型的undo log就会被系统直接删除回收,也就是该undo log占用的undo页面链表被释放)。
  • update undo log:update、delete的时候,产生的undo log日志不仅在事务回滚时需要,在快照读时也需要(也就是MVCC),所以不能在事务提交后马上删除,只在提交后放入undo log的链表,等待purge线程进行最后的删除。

事务需要保证原子性,也是说事务中的操作要么全部完成,要么什么也不做。如果事务执行到一半,出错了怎么办-回滚。但是怎么回滚呢,靠 undo 日志。undo 日志就是我们执行sql的逆操作

  • undo 日志有两个作用:提供回滚和多个行版本控制(MVCC)
  • 数据页里一行数据的格式 见3.3版本链,其中 roll_point 会指向一个undo 日志
  • undo 日志一般会在事务提交时被删除,但是如果 undo 日志为 MVCC 服务 则暂时保留
  • 一个事务会产生多个 undo 日志,mysql有专门的 undo 页 保存 undo 日志。innodb 会为每一个事务单独分配 undo 页链表(最多分配 4 个链表)

比如现在Tom的账户余额有100,现在有一个事务需要把Tom的账户余额更新为300,大致的流程如下图:

3.2 隐藏字段 —— 事务ID(TRX_ID)、ROLL_PTR

当我们创建了上面的这张表,我们在查看表结构的时候,就可以显式的看到 id、user_name、balance、wealth 这四个字段。 实际上除了这四个字段以外,InnoDB还会自动的给我们添加三个隐藏字段,分别是:

隐藏字段 含义
DB_TRX_ID 最近修改事务ID,记录插入这条记录或最后一次修改该记录的事务ID。
DB_ROLL_PTR 回滚指针,指向这条记录的上一个版本,用于配合undo log,指向上一个版本。
DB_ROW_ID 隐藏主键,如果表结构没有指定主键,将会生成该隐藏字段。

而上述的前两个字段是肯定会添加的, 是否添加最后一个字段DB_ROW_ID,得看当前表有没有主键,如果有主键,则不会添加该隐藏字段。

3.3 版本链

不同事务或相同事务对同一条记录进行修改,会导致该记录的undolog生成一条记录版本链表,链表的头部是最新的旧记录,链表尾部是最早的旧记录。

然后,有四个并发事务同时在访问这张表。


四、redo log 重做日志

事务日志redo log(重做日志):是Innodb存储引擎层生成的日志,实现了事务中的持久性,主要用于掉电等故障恢复。比如MySQL实例挂了或宕机了,重启时,InnoDB存储引擎会使用redo log恢复数据

  • redo log是物理日志,记录页的物理修改操作:在某个数据页做了什么修改,比如对x表空间中的N数据页ZZ偏移量的地方做了AAAA更新,每当执行一个事务就会产生这样的一条或者多条物理日志
  • 保证数据的持久性:redo log会在事务提交时将日志存储到磁盘redo log file,保证日志的持久性;同时mysql会将数据写入磁盘,保证数据的持久性【在事务提交时,只要先将redo log 持久化到磁盘即可,可以不需要等到将缓存在 Buffer Pool 里的脏页数据持久化到磁盘;当系统崩溃时,虽然脏页数据没有持久化,但是redo log已经持久化,接着MySQL重启后,可以根据redo log的内容,将所有数据恢复到最新的状态】

介绍下 缓冲池与数据页的概念

  • 缓冲池(buffer pool):主内存中的一个区域,里面可以缓存磁盘上经常操作的真实数据,在执行增删改查操作时,先操作缓冲池中的数据(若缓冲池没有数据,则从磁盘加载并缓存),以一定频率刷新到磁盘,从而减少磁盘IO,加快处理速度
  • 数据页(page):是InnoDB 存储引擎磁盘管理的最小单元,每个页的大小默认为 16KB。页中存储的是行数据

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. 事物提交后,也会将内存中修改的数据写入到磁盘。

4.1 redo log详解

什么是 redo log?为了方便理解,先举个来自极客时间的例子:

还记得《孔乙己》这篇文章,饭店掌柜有一个粉板,专门用来记录客人的赊账记录。如果赊账的人不多,那么他可以把顾客名和账目写在板上。但如果赊账的人多了,粉板总会有记不下的时候,这个时候掌柜一定还有一个专门记录赊账的账本

如果有人要赊账或者还账的话,掌柜一般有两种做法:

  • 一种做法是直接把账本翻出来,把这次赊的账加上去或者扣除掉;
  • 另一种做法是先在粉板上记下这次的账,等打烊以后再把账本翻出来核算。

在生意红火柜台很忙时,掌柜一定会选择后者,因为前者操作实在是太麻烦了。首先,你得找到这个人的赊账总额那条记录。你想想,密密麻麻几十页,掌柜要找到那个名字,可能还得带上老花镜慢慢找,找到之后再拿出算盘计算,最后再将结果写回到账本上。

这整个过程想想都麻烦。相比之下,还是先在粉板上记一下方便。你想想,如果掌柜没有粉板的帮助,每次记账都得翻账本,效率是不是低得让人难以忍受?

为什么需要 redo log?

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

跟上述案例类似,在 MySQL 中,如果每一次的更新要写进磁盘,这么做会带来严重的性能问题:

  • 因为 Innodb 是以页为单位进行磁盘交互的,而一个事务很可能只修改一个数据页里面的几个字节,这时将完整的数据页刷到磁盘的话,太浪费资源了!
  • 一个事务可能涉及修改多个数据页,并且这些数据页在物理上并不连续,使用随机 IO 写入性能太差

为了解决这个问题,MySQL 的设计者就用了类似掌柜粉板的思路来提升更新效率。这种思路在 MySQL 中叫 WAL(Write-Ahead Logging),意思就是:先写 redo log 日志,后写磁盘。日志和磁盘就对应上面的粉板和账本。

具体到 MySQL 是这样的:有记录需要更新,InnoDB 把记录写到 redo log 中,并更新内存中的数据页,此时更新就算完成。同时,后台线程会把操作记录更新异步到磁盘中的数据页。

PS:当需要更新的数据页在内存中时,就会直接更新内存中的数据页;不在内存中时,在可以使用 change buffer(篇幅有限,后面写文章再聊) 的情况下,就会将更新操作记录到 change buffer 中,并将这些操作记录到 redo log 中;如果此时有查询操作,则触发 merge 操作,返回更改后的记录值

有些人说 InnoDB 引擎把日志记录写到 redo log 中,redo log 在哪,不也是在磁盘上么?

对,这也是一个写磁盘的过程,但是与更新过程不一样的是,更新过程是在磁盘上随机 IO、费时, 而写 redo log 是在磁盘上顺序 IO、效率要高

PPS:redo log 的存在就是把全局的随机写,变换为局部的顺序写,从而提高效率

4.2 redo log的写入过程、刷盘时机

redo log 记录了事务对数据页做了哪些修改。它包括两部分:分别是内存中的日志缓冲(redo log buffer)和磁盘上的日志文件(redo log file)。

mysql 每执行一条 DML 语句,先将记录写入 redo log buffer,后续某个时间点再一次性将多个操作记录写到 redo log file。也就是我们上面提到的 WAL 技术。

计算机操作系统告诉我们:用户空间下的缓冲区数据是无法直接写入磁盘的。因为中间必须经过操作系统的内核空间缓冲区(OS Buffer)。

所以,redo log buffer 写入 redo logfile 实际上是先写入 OS Buffer,然后操作系统调用 fsync() 函数将日志刷到磁盘。过程如下:

mysql 支持三种将 redo log buffer 写入 redo log file 的时机,可以通过 innodb_flush_log_at_trx_commit 参数配置,各参数值含义如下:建议设置成1,这样可以保证MySQL 异常重启之后数据不丢失

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

4.3 redo log file 的结构

InnoDB 的 redo log 是固定大小的。比如可以配置为一组 4 个文件,每个文件的大小是 1GB,那么 redo log file 可以记录 4GB 的操作。从头开始写。写到末尾又回到开头循环写。如下图:

上图中,write pos 表示 redo log 当前记录的 LSN (逻辑序列号) 位置,一边写一遍后移,写到第 3 号文件末尾后就回到 0 号文件开头; check point 表示数据页更改记录刷盘后对应 redo log 所处的 LSN(逻辑序列号) 位置,也是往后推移并且循环的。

PS:check point 是当前要擦除的位置,它与数据页中的 LSN 应当是一致的

write pos 到 check point 之间的部分是 redo log 的未写区域,可用于记录新的记录;check point 到 write pos 之间是 redo log 已写区域,是待刷盘的数据页更改记录。

当 write pos 追上 check point 时,表示 redo log file 写满了,这时候有就不能执行新的更新。得停下来先擦除一些记录(擦除前要先把记录刷盘),再推动 check point 向前移动,腾出位置再记录新的日志。

4.4 什么是 crash-save

有了 redo log ,即在 InnoDB 存储引擎中,事务提交过程中任何阶段,MySQL 突然奔溃,重启后都能保证事务的完整性,已提交的数据不会丢失,未提交完整的数据会自动进行回滚。这个能力称为 crash-safe,依赖的就是 redo log 和 undo log 两个日志。

比如:重启 innodb 时,首先会检查磁盘中数据页的 LSN ,如果数据页的 LSN 小于日志中 check point 的 LSN ,则会从 checkpoint 开始恢复。

4.5 redo log细节

  • redo log是物理日志(并非sql执行语句,sql语句是逻辑操作),顺序写入,性能比较高
  • 重做日志是innodb存储引擎产生的
  • 重做日志由redo logo buffer和redo log file构成,即由 内存中的重做日志缓存、日志文件构成

五、补充

5.1 总结、使用场景

  • 二进制binlog(归档日志):是Server层生成的日志,主要用于数据备份和主从复制;
  • undolog(回滚日志):是Innodb存储引擎层生成的日志,实现了事务中的原子性,主要用于事务回滚和MVCC
  • 事务日志redolog(重做日志):是Innodb存储引擎层生成的日志,实现了事务中的持久性,主要用于掉电等故障恢复

binlog的应用场景

  • 主从复制:在Master端开启binlog,然后将binlog 发送到各个Slave端,Slave端重放binlog从而达到主从数据一致。
  • 数据恢复:通过使用mysqlbinlog工具来恢复数据。

undolog的使用场景

  • 事务回滚
  • MVCC

redolog的使用场景

掉电等故障恢复

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

主从:写数据时只写主库,在读数据时只读从库,这样即使写请求会锁表或者锁记录,也不会影响读请求的执行;

5.2 binlog与redolog对比、逻辑日志与物理日志

redo log 和 binlog 主要有三种不同:

  • redo log 是 InnoDB 引擎特有的;binlog 是 MySQL 的 Server 层实现的,所有引擎都可以使用。
  • redo log 是物理日志(并非sql执行语句,sql语句是逻辑操作),记录的是在某个数据页上做了什么修改,比如"对XXX表空间中的YYY数据页ZZZ偏移量的地方做了AAA更新" ;binlog 是逻辑日志,记录的是这个语句的原始逻辑,比如 "给 ID=2 这一行的 age 字段加1"
  • redo log 是循环写的,空间固定会用完;binlog是可以追加写入的。追加写是指 binlog文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。

逻辑日志:可以简单理解为记录的就是sql语句

物理日志:因为mysql数据最终是保存在数据页中的,物理日志记录的就是数据页变更

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

不可以使用redo log文件恢复,只能使用binlog文件恢复。
因为redo log文件是循环写,是会边写边擦除日志的,只记录未被刷入磁盘的数据的物理日志,已经刷入磁盘的数据都会从redo log文件里擦除;
binlog文件保存的是全量的日志,也就是保存了所有数据变更的情况,理论上只要记录在binlog上的数据,都可以恢复,所以如果不小心整个数据库的数据被删除了,得用binlog文件恢复数据。

5.3 update语句的执行流程

了解了binlog、redolog两种日志的概念,再来看看执行器和 InnoDB 引擎在执行 update 语句时的流程:

  • 执行器取 id = 2 的行数据。ID 是主键,引擎用树搜索找到这一行。如果这一行所在的数据页本来就在内存中,就直接返回给执行器;否则,需要先从磁盘读入内存,再返回。
  • 执行器拿到引擎给的行数据,把这个值加上 1,比如原来是 N,现在就是 N+1,得到新的一行数据,再调用引擎接口写入这行新数据。
  • 引擎将这行新数据更新到内存中,同时将这个更新操作记录到 redo log 里面,此时 redo log 处于 prepare 状态。然后告知执行器执行完成了,随时可以提交事务。
  • 执行器生成这个操作的 binlog,并把 binlog 写入磁盘。
  • 执行器调用引擎的提交事务接口,引擎把刚刚写入的 redo log 改成提交(commit)状态,redo log 会写入 binlog 的文件名和位置信息来保证 binlog 和 redo log 的一致性,更新完成。

整个过程如下图所示,其中橙色框表示是在 InnoDB 内部执行的,绿色框表示是在执行器中执行的:

5.4 两阶段提交

在执行更新语句过程中,会记录redolog、binlog两块日志,以基本的事务为单位。

redolog在事务执行过程中可以不断写入,binlog只在提交事务时写入

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

从图中可以看出,在最后提交事务的时候,有3个步骤:

  1. 写入redo log,处于prepare状态
  2. 写binlog
  3. 修改redo log状态变为commit

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

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

为什么需要两阶段提交?

由于 redo log 和 binlog 是两个独立的逻辑,如果不用两阶段提交,要么就是先写完 redo log 再写 binlog,或者采用反过来的顺序。我们看看这两种方式会有什么问题(redo log与bin log两份日志之间的逻辑不一致,会出现什么问题)。

仍然用前面的 update 语句来做例子,update table set age=age+1 where id=2。假设当前 id=2 的行,字段 age 的值是 22,再假设执行update 语句过程中在写完第一个日志后,第二个日志还没有写完期间发生了 crash,会出现什么情况呢?

  1. 先写redo log 后写binlog。假设在redo log写完,binlog 还没有写完的时候,MySQL进程异常重启。由于我们前面说过的,redo log 写完之后,系统即使崩溃,仍然能够把数据恢复回来,所以恢复后这一行 age 的值是 22。
  • 但是 binlog 没写完就 crash 了,会出现什么情况呢?由于binlog没写完就异常,这时 binlog 里面并没有记录这个修改语句。
    因此,等到之后需要用binlog日志备份数据的时候,存起来的 binlog 里面没有这条语句,这个临时库就会少这一次更新,恢复出来的这一行age值是22;而原库因为redo log日志恢复,这一行age值是23,最终数据不一致【为了解决两份日志之间的逻辑一致问题,InnoDB存储引擎使用两阶段提交方案】

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

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

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

  1. 先写 binlog 后写 redo log。如果在 binlog 写完之后 crash,由于 redo log 还没写,崩溃恢复以后这个事务无效,所以 age 的值是 22。但是 binlog 里面已经记录了"把从 22 改成 23" 这个日志。所以,在之后用 binlog 来恢复的时候就多了一个事务出来,恢复出来的这一行 age 的值就是 23,与原库的值不同。

所以,如果不使用"两阶段提交",数据库的状态就有可能和用 binlog 恢复出来的不一致。

另外:sync_binlog 这个参数建议设置成 1,表示每次事务的binlog都持久化到磁盘,这样可以保证MySQL异常重启之后 binlog 不丢失

5.5 MySQL主从复制

MySQL主从复制的核心就是二进制日志binlog

二进制日志(BINLOG)记录了所有的 DDL(数据定义语言,创建库、表)语句和 DML(数据操纵语言,增删改)语句,但不包括数据查询(SELECT、SHOW)语句。

5.5.1 作用

  • 读写分离
  • 数据备份
  • 高可用性

5.5.2 原理

实际上主从同步是基于binlog进行数据同步的。在主从复制过程中,会基于3个线程来操作,一个主库线程、两个从库线程

  • 二进制日志转储线程(Binglog dump thread)是一个主库线程,当从库线程连接时,主库可以将二进制日志发送给从库,当主库读取事件时,会在Binlog上加锁,读取完成后释放锁
  • 从库I/O线程连接主库,向主库发送请求更新Binlog。这时从库的I/O线程就可以读取主库的二进制日志转储线程的Binlog更新部分,并且拷贝到本地的中继日志relaylog
  • 从库SQL线程会读取从库中的中继日志,并且执行日志中的事件,将从库中的数据与主库保持同步

复制过程就是将binlog中的数据从主库传输到从库上,这个过程一般是异步的,即主库上执行事务操作的线程不会等待复制binlog的线程同步完成。主从复制概括为 :在Master端开启binlog ,从库将master的binlog拷贝到它的中继日志relay log,slave端重放binlog 从而达到主从数据一致

简单来说分成三步:

  1. 写入binlog:Master 主库在事务提交时,会把数据变更记录在二进制日志文件 Binlog 中
  2. 同步binlog:从库读取主库的二进制日志文件 Binlog ,写入到从库的中继日志 Relay Log(从库将master的binlog拷贝到它的中继日志)
  3. 回放binlog:slave重做中继日志中的事件,将改变应用到自己的数据库中

MySQL复制时异步且串行化的,重启后从接入点开始复制。


具体详细过程如下:

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

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

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

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

5.5.3 同步数据一致性

主从同步要求

  • 读库、写库的数据一致
  • 写数据必须写到写库
  • 读数据必须到读库

主从延迟原因

网络正常情况下,主从延迟的主要来源是备库接收完binlog和执行完这个事务之间的时间差

  • 从库的机器性能比主库差
  • 从库压力大
  • 大事务的执行

主从延迟的直接表现:从库消费中继日志的速度比主库生产binlog的速度慢

减少主从延迟的方案

  • 降低多线程大事务并发的概率,优化业务逻辑
  • 优化SQL,避免慢SQL,减少批量操作,建议写脚本以update-sleep这样的形式完成
  • 提高从库机器配置,减少主库写binlog和从库读binlog的效率差
  • 尽量采用短的链路,也就是主库和从库服务器的距离尽量短,提升端口带宽,减少binlog传输的网络延时
  • 实时性要求的业务强制走主库,从库只做灾备、备份

数据一致性问题的解决

若操作的数据存储在同一个数据库中,那么对数据进行更新时,可对记录加写锁,这样在读取时就不会发生数据不一致的情况,但从库的作用仅为备份,未起到读写分离、分担主库读压力的作用

读写分离情况下,解决主从同步中数据不一致的问题,就是解决主从之间数据复制方式的问题。若按照数据一致性的从弱到强划分,有3种复制方式:异步复制、半同步复制、组复制

异步复制

半同步复制

组复制

异步复制、半同步复制都无法最终保证数据一致性问题

组复制技术,MRG(MySQL Group Replication),于MySQL在5.7.17推出的一种新的数据复制技术,基于Paxos协议的状态机复制

MGR如何工作?

将多个节点共同组成一个复制组,在执行读写(RW)事务时,需要通过一致性协议层同意,当同意节点数量大于(N/2+1)时才可进行提交,针对只读(RO)事务则不需要组内同意,直接COMMIT即可

在一个复制组内有多个节点组成,它们各自维护了自己的数据副本,并且在一致性协议层实现了原子消息和全局有序消息,从而保证组内数据一致性

参考:

MySQL面试题(最全、超详细)—— 定位慢查询、undo log与redo log

MVCC 原理分析、MySQL是如何解决幻读的

《MySQL》系列 - 十张图详解 MySQL 日志(建议收藏)

MySQL进阶(日志)——MySQL的日志 & bin log (归档日志) & 事务日志redo log(重做日志) & undo log(回滚日志)

MySQL面试资料

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/879829.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用miniforge代替miniconda

conda作为Python数据科学领域的常用软件,是对Python环境及相关依赖进行管理的经典工具,通常集成在anaconda或miniconda等产品中供用户日常使用。但长久以来,conda在很多场景下运行缓慢卡顿、库解析速度过慢等问题也一直被用户所诟病,且由于anaconda、miniconda本身属于商业…

AI边坡监测摄像头

AI边坡监测摄像头通常安装在易发生滑坡或崩塌的区域,通过持续拍摄周围环境。当系统捕捉到异常情况(如裂缝扩大、土壤位移等)时,它会立即进行数据分析。如果确认存在危险迹象,系统将自动触发报警,并通知相关管理人员。该设备利用深度学习算法对视频流进行实时处理,可以精…

每日练习 25.2.6

Nastia and a Hidden Permutation 题目 娜斯佳有一个长度为 \(n\) 的隐藏排列 \(p\),由从 \(1\) 到 \(n\) 的整数组成。出于某种原因,你想找出这个排列。为此,您可以给她一个整数 \(t\) \(( 1 \le t \le 2 )\),两个不同的索引 \(i\) 和 \(j\) \(( 1 \le i, j \le n ,i \neq…

2025多校冲刺省选模拟赛9

2025多校冲刺省选模拟赛9\(T1\) A. 鸬鹚 \(20pts\)部分分\(30pts\)顺次迭代,并查集优化不明显。判断矩形相交时在特判掉包含后可以直接代入四个角进行判断。点击查看代码 struct node {int x1,x2,y1,y2;node operator + (const node &another) const{return (node){min(x1…

KVM在传递虚拟机镜像到时候的错误的解决方案An auth plugin is required to determine endpoint URL

最近在玩Openstack和KVM, Openstack是用devstack安装的, 但是在用kvm创建镜像到Openstack的时候出现这个错误,An auth plugin is required to determine endpoint URL,参考 了网上所有的方案都失败了,自己最终尝试用这个方式解决了,分享给需要的朋友。 修改/opt/admin-op…

IDEA 接入 DeepSeek,太酷了!

你好,我是 Guide。前两天,我发文提到 DeepSeek 的回答推荐了我的资料 《JavaGuide 面试突击版》(PDF):DeepSeek 的这波回答我给满分!。 这篇文章简单介绍一下如何在常用的 IDE 中接入 DeepSeek,这里以 Java 开发最常用的 IDEA 为例。 目前,IDEA 中接入 DeepSeek,可以借…

社媒风控下的生存法则:云手机如何为Facebook、X(Twitter)账号安全护航

社媒风控下的生存法则:云手机如何为Facebook、X(Twitter)账号安全护航 在社交媒体平台(如Facebook、X/Twitter)风控日益严格的背景下,账号安全成为运营者的核心痛点。平台通过检测设备指纹、IP地址、SIM卡信息等参数,对批量操作、异常登录或虚拟环境进行封号限制。云手机…

还在为标准“头秃”?数字化转型秘籍来了!

数字化浪潮席卷而来,企业都想“乘风破浪”,可标准化管理这块“基石”要是没打牢,小心“翻船”哦!别担心,我们带着标准制修订信息管理系统来拯救你啦!这可不是一般的系统,它能让你的标准化工作从“石器时代”直接跃迁到“赛博朋克”!😫 标准化管理的“老大难”:你中了…

[megatron代码阅读] 2. TP和PP实现

megatron 源码阅读第二篇, 看了TP和PP的对应实现训练并行实现 TensorParallel 张量并行代码路径, 代码路径: megatron/core/tensor_parallel 主要包含Linear / VocabEmbedding / cross_entropy 三部分. Linear 参数初始化 如果是从checkpoint热启, perform_initialization需要打…

2024.2.6鲜花

初探牛顿迭代?推歌 《以恋结缘》 诚、意地の悪い神の所业か? 奇迹?縁?袂触合う不思议 花ひとひら揺れて 不意に宿ってた うなじ解いてく春风 戯れはそこそこに 恋手ほどきしてくだしゃんせ 汤気にほんのり頬染て 夜风に愿ふ …いざ!!蝶と舞ひ花となりて 衣を乱して祓いま…