MySQL事务日志-Undo Log工作原理分析

本文涉及的产品
RDS MySQL DuckDB 分析主实例,集群系列 4核8GB
RDS AI 助手,专业版
简介: 事务的持久性是交由Redo Log来保证,原子性则是交由Undo Log来保证。如果事务中的SQL执行到一半出现错误,需要把前面已经执行过的SQL撤销以达到原子性的目的,这个过程也叫做"回滚",所以Undo Log也叫回滚日志。

2.1 Undo Log

2.1.1 Undo Log与原子性

事务的持久性是交由Redo Log来保证,原子性则是交由Undo Log来保证。如果事务中的SQL执行到一半出现错误,需要把前面已经执行过的SQL撤销以达到原子性的目的,这个过程也叫做"回滚",所以Undo Log也叫回滚日志。

Undo Log记录了数据在每个操作前的状态,这些记录包括旧的数据值和事务的 ID。如果事务执行过程中需要回滚,就可以根据Undo Log进行回滚操作。

每当我们要对一条记录做改动时(这里的改动可以指INSERT、DELETE、UPDATE),把回滚时所需的东西记下来。比如:

  • 当执行了一条insert语句时:至少要把这条记录的主键值记下来,之后回滚的时候只需要把这个主键值对应的记录删掉就好了。对于事务中的每个insert语句,在事务回滚时InnoDB都会完成一个delete操作。
  • 当执行了一条delete语句时:至少要把这条记录中的内容都记下来,这样之后回滚时再把由这些内容组成的记录插入。对于事务中的每个delete语句,在事务回滚时InnoDB都会完成一个insert操作。
  • 当执行了一条update语句时:至少要把修改这条记录前的旧值都记录下来,这样之后回滚时再把这条记录更新为旧值。对于事务中的每个update语句,在事务回滚时InnoDB都会完成一个反向的update操作。

另外,Undo Log 也是实现多版本并发控制的基础,通过保存旧版本的数据,InnoDB 可以在并发事务中提供隔离级别,如读已提交(Read Committed)、可重复读(Repeatable Read)等。

Tips:Undo Log主要保证事务的原子性,即通过记录修改前的状态,以提供回滚功能,其次Undo Log用于提供MVCC的快照读。

2.1.2 Undo的存储格式

Redo属于物理日志,即记录了数据库页的物理修改操作,比如页上的哪些字节被更改,具体到物理结构上。Undo属于逻辑日志,即记录了从逻辑角度如何撤销已经发生的变更的信息,第一步第二步该如何做等,通常包括了反向操作所需要的数据。

Tips:“Redo属于物理日志”意味着它详细记录了物理层面的数据页是如何被改变的;而“Undo属于逻辑日志”则表示它更多是从逻辑角度出发,记录了为实现某种目的(如回滚或访问历史版本)所需执行的操作步骤。这两者共同保障了MySQL数据库中事务处理的ACID特性。

在InnoDB中,所有表中都会有三个隐藏的列,分别为:DB_ROW_ID、DB_TRX_ID、DB_ROLL_PTR。

  • DB_TRX_ID:数据行版本号,也叫事务ID。当有新的数据修改或插入时的事务ID号,用于记录修改这条记录的事务ID和创建这条记录的事务ID。(记录这条数据是哪个事务修改的,哪个事务创建的)
  • DB_ROLL_PTR:删除行版本号,也叫回滚指针。指向Undo Log中这条记录的上一个版本,删除记录,记录当前事务ID。(记录这条数据是哪个事务删除的)
  • DB_ROW_ID:聚集索引。如果数据表没有主键,InnoDB会创建一个DB_ROW_ID作为聚集索引。

其中事务ID与回滚指针和Undo Log日志密切相关,另外在InnoDB中,Undo Log分为Insert Undo Log和update Undo Log两种类型,其中删除操作也是借助update Undo Log来完成的。

1)insert类型Undo Log

执行如下insert语句:

insert into user values(1,"小灰",20);

当事务开启后,执行的insert语句都会以“insert类型的undo log”记录在undo log日志中,本次事务插入的所有的数据行的版本号字段都为当前事务的ID。

如果需要进行事务回滚,根据undo log中记录的主键进行delete操作即可。

2)delete类型Undo Log

执行如下delete语句:

delete from user where id=1;

当事务开启后,InnoDB对于delete语句的流程是:

  • 1)将被删除的行以“update类型的undo log”记录到undo log日志中。
  • 2)将该行的事务ID设置为当前事务ID,回滚指针设置为被删除前那条记录的事务ID。
  • 3)将删除标记设置为1,表示该记录是被删除掉的记录。
  • 4)更改表空间。

3)update类型Undo Log

执行如下update语句:

update user set name="小绿" where id=1;

当事务开启后,InnoDB对于update语句的流程是:

  • 1)将被修改的行以“update类型的undo log”记录到undo log日志中。
  • 2)将该行的事务ID设置为当前事务ID,回滚指针设置为被删除前那条记录的事务ID。
  • 3)更改表空间。

当同一条数据被修改多次,那么Undo Log将通过数据的事务ID和回滚指针能够形成一个非常好的修改链路:

2.1.3 Undo Log的工作原理

InnoDBMySQL启动时,会在内存中构建一个BufferPool,而这个缓冲池主要存放两类东西,一类是数据相关的缓冲,如索引、锁、表数据等,另一类则是各种日志的缓冲,如Undo、Redo....等日志。当一条写SQL执行时,MySQL并不会直接去往磁盘中的ibd文件写数据,而是先修改内存中的Buffer Pool,这样性能就能得到极大的提升。

与Redo Log一样,Undo Log也存在内存缓冲区,即Undo Log Buffer。当一条写SQL执行时,不会直接去往磁盘中的xx.ibdata文件中的Undo Log写数据,而是会写在undo_log_buffer缓冲区中,因为工作线程直接去写磁盘太影响效率了,写进缓冲区后会由后台线程去刷写磁盘。

  • Undo Log完整的工作原理如下:

首先在操作表时,会将表数据从磁盘(.idb)加载到内存中(Buffer),对表的update/delete等操作InnoDB都会事先将修改前的数据备份到Undo Buffer中,这样当事务进行回滚时可以根据Undo Buffer中的内容进行事务的回滚操作,除此之外,Undo Buffer提供了数据的快照读取,在事务未提交之前,Undo 日志可以作为并发读写时的读快照,来保证事务的可重复读;

事务做到一半了,失败了,那就要将数据还原到未提交之前的状态,undo 就是记录这些事务步骤的。当然了redo 也记录了,但是redo里面东西太繁杂,不可能什么事都找它(主要是Redo和Undo的格式不一样,应用场景也不一样),于是就将事务步骤写入另外一个地方:undo,以后遇到回滚了就去查找因此在每一步操作时都会写入磁盘中的Undo Log;

引入Undo Log Buffer是来提升Undo Log的性能的,比较操作内存要比操作磁盘快多了,但由此也引入了另外一个问题,那就是既然内存中记录了Undo Log的值,为什么还要在磁盘中也记录Undo Log的值呢?难道Undo Log也要保证持久性?

并不是,在InnoDB中持久性由Redo保证。Undo之所以要写人磁盘是因为InnoDB对数据进行了多版本的控制(MVCC)。

观察如下案例:

session-01 session-02
begin;
begin;
select * from user;
update user set name='小蓝' where id=1;
commit;
select * from user; -- 能否查询到修改的数据?

如果当前的事务隔离级别为RR(可重复读),那么在session-02事务中修改的数据是不能被查询出来。那数据库表中id=1的这一行数据的name值有没有被改为"小蓝"呢?答案是肯定的,因为事务都已经提交了,磁盘表中的name已经修改为了"小蓝"。这就引入了一个问题,并不是提交的数据就一定能被查询出来的,有时候需要查询旧数据。Undo Log正是保存那些旧版本的数据,让其在其他事务中可见。

2.1.4 Undo Log的系统参数

InnoDBundo log的管理采用段的方式,也就是回滚段(rollback segment) 。每个回滚段记录了 1024 个 undo log segment,每个事务只会使用一个回滚段,当一个事务开始的时候,会制定一个回滚段,在事务进行的过程中,当数据被修改时,原始的数据会被复制到回滚段。

在MySQL5.5的时候,只有一个回滚段,那么最大同时支持的事务数量为1024个。在MySQL 5.6开始,InnoDB支持最大 128个回滚段,故其支持同时在线的事务限制提高到了 128*1024 。

我们可以查看InnoDB中Undo Log的有关系统参数,在MySQL5.5之前没有太多参数,如下:

mysql> show variables like 'innodb_max_undo_log_size';
+--------------------------+------------+
| Variable_name            | Value      |
+--------------------------+------------+
| innodb_max_undo_log_size | 1073741824 |
+--------------------------+------------+
1 row in set (0.00 sec)

mysql> show variables like 'innodb_rollback_segments';
+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| innodb_rollback_segments | 128   |
+--------------------------+-------+
1 row in set (0.00 sec)
  • innodb_max_undo_log_size:本地磁盘文件中,Undo-log的最大值,默认1GB
  • innodb_rollback_segments:指定回滚段的数量,默认为1个。

除开上述两个参数外,其他参数基本上是在MySQL5.6才有的,如下:

mysql> show variables like '%innodb_undo%';
+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| innodb_undo_directory    | ./    |
| innodb_undo_log_truncate | OFF   |
| innodb_undo_logs         | 128   |
| innodb_undo_tablespaces  | 0     |
+--------------------------+-------+
4 rows in set (0.00 sec)
  • innodb_undo_directory: 设置rollback segment文件所在的路径。这意味着rollback segment可以存放在共享表空间以外的位置,即可以设置为独立表空间。该参数的默认值为“/”,即MySQL的数据文件夹。
  • innodb_undo_logs: 设置rollback segment的个数,默认值为128,也就是之前的innodb_rollback_segments
  • innodb_undo_tablespaces: 设置构成rollback segment文件的数量,这样rollback segment可以较为平均地分布在多个文件中。设置该参数后,会在路径innodb_undo_directory看到undo为前缀的文件,该文件就代表rollback segment文件。
  • innodb_undo_log_truncate:是否开启Undo-log的在线压缩功能,即日志文件超过大小一半时自动压缩,默认OFF关闭。

MySQL5.5版本以后,Undo-log日志支持单独存放,并且多出了几个参数可以调整Undo-log的区域。

2.1.5 Undo Log与Purge线程

前面提到,事务提交后Undo Log日志并不会马上删除,因为其他事务很可能需要用到该数据。直接移除可能会导致其他事务读不到数据。那么对于废弃的undo log日志在什么时候删除呢?另外磁盘表中的被标记为删除的记录(数据空洞),也需要进行空间释放。这些数据都是由MySQL内部的线程——Purge线程来执行后台删除。

  • 针对于insert undo log,因为insert操作的记录,只对事务本身可见,对其他事务不可见。故该undo log在事务提交后就没有用,就会直接删除。
  • 针对于update undo log,该undo log需要支持MVCC机制,因此不能在事务提交时就进行删除。提交时放入undo log链表,有专门的purge线程进行删除。

有关于Purge线程的参数:

mysql> show variables like '%purge%';
+--------------------------------------+-------+
| Variable_name                        | Value |
+--------------------------------------+-------+
| gtid_purged                          |       |
| innodb_max_purge_lag                 | 0     |
| innodb_max_purge_lag_delay           | 0     |
| innodb_purge_batch_size              | 300   |
| innodb_purge_rseg_truncate_frequency | 128   |
| innodb_purge_threads                 | 4     |
| relay_log_purge                      | ON    |
+--------------------------------------+-------+
7 rows in set (0.01 sec)
  • innodb_max_purge_lag:当InnoDB存储引擎压力非常大时,Purge线程可能并不会工作,此时是否要延缓DML的操作,innodb_max_purge_lag控制Undo Log的数量,如果数量大于该值,就延缓DML的操作,默认为0,代表不延缓;
  • innodb_max_purge_lag_delay:表示当上面innodb_max_purge_lag的delay超时时间太大,超过这个参数时,将delay设置为该参数值,防止purge线程操作缓慢导致其他SQL线程长期处于等待状态。默认为0,一般不用修改。
  • innodb_purge_batch_size:用来设置每次purge操作需要清理的Undo Log page的数量。
  • innodb_purge_threads:Purge线程的数量(默认为4,最大为32)
相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。   相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情: https://www.aliyun.com/product/rds/mysql 
相关文章
|
5月前
|
SQL 关系型数据库 MySQL
MySQL锁机制:并发控制与事务隔离
本文深入解析了MySQL的锁机制与事务隔离级别,涵盖锁类型、兼容性、死锁处理及性能优化策略,助你掌握高并发场景下的数据库并发控制核心技巧。
|
6月前
|
存储 监控 Oracle
MySQL事务
MySQL事务具有ACID特性,包括原子性、一致性、隔离性和持久性。其默认隔离级别为可重复读,通过MVCC和间隙锁解决幻读问题,确保事务间数据的一致性和并发性。
MySQL事务
|
4月前
|
关系型数据库 MySQL 数据库
【赵渝强老师】MySQL的事务隔离级别
数据库并发访问时易引发数据不一致问题。如客户端读取到未提交的事务数据,可能导致“脏读”。MySQL通过四种事务隔离级别(读未提交、读已提交、可重复读、可序列化)控制并发行为,默认为“可重复读”,以平衡性能与数据一致性。
321 0
|
5月前
|
关系型数据库 MySQL 数据库
MySql事务以及事务的四大特性
事务是数据库操作的基本单元,具有ACID四大特性:原子性、一致性、隔离性、持久性。它确保数据的正确性与完整性。并发事务可能引发脏读、不可重复读、幻读等问题,数据库通过不同隔离级别(如读未提交、读已提交、可重复读、串行化)加以解决。MySQL默认使用可重复读级别。高隔离级别虽能更好处理并发问题,但会降低性能。
216 0
|
XML 安全 Java
【日志框架整合】Slf4j、Log4j、Log4j2、Logback配置模板
本文介绍了Java日志框架的基本概念和使用方法,重点讨论了SLF4J、Log4j、Logback和Log4j2之间的关系及其性能对比。SLF4J作为一个日志抽象层,允许开发者使用统一的日志接口,而Log4j、Logback和Log4j2则是具体的日志实现框架。Log4j2在性能上优于Logback,推荐在新项目中使用。文章还详细说明了如何在Spring Boot项目中配置Log4j2和Logback,以及如何使用Lombok简化日志记录。最后,提供了一些日志配置的最佳实践,包括滚动日志、统一日志格式和提高日志性能的方法。
4308 31
【日志框架整合】Slf4j、Log4j、Log4j2、Logback配置模板
|
9月前
|
监控 容灾 算法
阿里云 SLS 多云日志接入最佳实践:链路、成本与高可用性优化
本文探讨了如何高效、经济且可靠地将海外应用与基础设施日志统一采集至阿里云日志服务(SLS),解决全球化业务扩展中的关键挑战。重点介绍了高性能日志采集Agent(iLogtail/LoongCollector)在海外场景的应用,推荐使用LoongCollector以获得更优的稳定性和网络容错能力。同时分析了多种网络接入方案,包括公网直连、全球加速优化、阿里云内网及专线/CEN/VPN接入等,并提供了成本优化策略和多目标发送配置指导,帮助企业构建稳定、低成本、高可用的全球日志系统。
954 54
|
监控 安全 Apache
什么是Apache日志?为什么Apache日志分析很重要?
Apache是全球广泛使用的Web服务器软件,支持超过30%的活跃网站。它通过接收和处理HTTP请求,与后端服务器通信,返回响应并记录日志,确保网页请求的快速准确处理。Apache日志分为访问日志和错误日志,对提升用户体验、保障安全及优化性能至关重要。EventLog Analyzer等工具可有效管理和分析这些日志,增强Web服务的安全性和可靠性。
434 9
|
XML JSON Java
Logback 与 log4j2 性能对比:谁才是日志框架的性能王者?
【10月更文挑战第5天】在Java开发中,日志框架是不可或缺的工具,它们帮助我们记录系统运行时的信息、警告和错误,对于开发人员来说至关重要。在众多日志框架中,Logback和log4j2以其卓越的性能和丰富的功能脱颖而出,成为开发者们的首选。本文将深入探讨Logback与log4j2在性能方面的对比,通过详细的分析和实例,帮助大家理解两者之间的性能差异,以便在实际项目中做出更明智的选择。
1426 3
|
存储 SQL 关系型数据库
MySQL日志详解——日志分类、二进制日志bin log、回滚日志undo log、重做日志redo log
MySQL日志详解——日志分类、二进制日志bin log、回滚日志undo log、重做日志redo log、原理、写入过程;binlog与redolog区别、update语句的执行流程、两阶段提交、主从复制、三种日志的使用场景;查询日志、慢查询日志、错误日志等其他几类日志
926 35
MySQL日志详解——日志分类、二进制日志bin log、回滚日志undo log、重做日志redo log

推荐镜像

更多