程序员求职经验分享与学习资料整理平台

网站首页 > 文章精选 正文

(二十五) 序员知识库 ! Mysql的事务和底层原理+ 常见面试题解析

balukai 2025-05-09 17:02:38 文章精选 7 ℃

MySQL事务是一组不可分割的数据库操作单元,通过ACID特性(原子性、一致性、隔离性、持久性)确保数据操作的完整性和可靠性,要么全部成功提交,要么全部回滚到初始状态,典型应用如银行转账、订单支付等需多步骤保障一致性的场景。这是事务的基本概念,今天阳仔就带大家深度学习一下事务的哪些事.

什么是事务?

事务是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败。

事务有哪些特性?

总体概括就是ACID ( 原子性、一致性、隔离性、持久性)

官方定义:

  • 原子性(Atomicity):事务是不可分割的最小操作单元,要么全部成功,要么全部失败。
  • 一致性(Consistency):事务完成时,必须使所有的数据都保持一致状态。
  • 隔离性(Isolation):数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行。
  • 持久性(Durability):事务一旦提交或回滚,它对数据库中的数据的改变就是永久的。

我们都知道事务的4个特性,那么这四个特性底层原理又是什么? 在讲解特性原理之前,我们需要熟悉一下几个关键概念Redo Log , Undo Log ,MVCC

Redo Log 是什么 ?

Redo Log(重做日志): InnoDB存储引擎的核心日志,以物理方式记录事务对数据页的修改操作,确保事务的持久性(Durability),是崩溃恢复(Crash Recovery)的核心机制。

组成重做日志缓冲(redo log buffer)以及重做日志文件(redo log file).

类比我们的redis 和数据库 redo log buffer 就相当于redis 缓存再降低了直接入库的IO也提高了并发性能,redo log file 就是我们的数据库文件在磁盘中

核心作用:

  • 事务提交: 保证事务提交后数据不丢失(即使系统崩溃)
  • 崩溃恢复: 重启后通过Redo Log重放未刷盘的修改操作,恢复数据到最新状态
  • 异步刷盘优化: 允许脏页延迟写入磁盘,提升事务提交效率

Redo Log 的工作原理:

  • 数据修改时:mysql将修改写入到Redo Log 中,而不是直接进入磁盘数据文件中,降低了IO操作的次数,提高了性能
  • 事务提交时: 再异步将数据写入磁盘中保证数据的持久性
  • 系统异常时: mysql也可以通过Redo Log 中的信息重做之前未被持久化的数据操作,恢复事务的提交状态

写入具体流程:

  • 日志生成: 事务修改数据页时,生成Redo Log记录

(物理日志,如Page=5, Offset=100, Data=0x1234)。

  • 日志缓冲 :Redo Log先写入内存的Log Buffer(避免频繁磁盘IO)。
  • 日志刷盘

根据
innodb_flush_log_at_trx_commit参数决定刷盘策略:

  • 1(默认):事务提交时同步刷盘(最高安全)。
  • 2:每秒异步刷盘(可能丢失1秒数据)。
  • 0:依赖后台线程刷盘(性能最高,风险最大)。

崩溃恢复流程:

  • 检查Checkpoint: 找到最后一次成功刷盘的Checkpoint位置(标记已持久化的数据)。
  • 重放日志: 从Checkpoint开始扫描Redo Log,重放所有未刷盘的修改到数据页。
  • 前滚完成: 数据页恢复到崩溃前的已提交状态。

关键参数与优化

优化建议:

  • 大事务拆分:避免单个事务产生过多Redo Log,导致日志文件快速轮转。
  • SSD适配:使用NVMe SSD提升日志写入速度,降低innodb_flush_log_at_trx_commit=1的性能损耗。
  • 监控指标:
SHOW ENGINE INNODB STATUS;  -- 查看LOG模块的写入情况
SHOW GLOBAL STATUS LIKE 'Innodb_os_log%';  -- 监控日志IO

Undo Log 是什么 ?

定义: 回滚日志,用于记录数据被修改前的信息.主要实现事务的一致性和原子性

作用包含两个 : 提供回滚 MVCC(多版本并发控制) 。undo log和redo log记录物理日志不一样,它是逻辑日志。

注意: 在增删改操作时候产生,insert 事务提交的时候会删除, 更新和删除操作,回滚和mvcc访问都需要,不会立即删除.

不同事务对同一条记录进行修改,系统会生产一个undo log 的版本链表,头部是最行新的记录,尾部是最早的记录(这个也是由MVCC 的原理之一)

例如:

 一个delete记录是时,Undo Log 会记录一条insert 记录,和实际操作的步骤是相反的.
 当事务要回滚的时候,直接执行该记录即可,删除的记录又恢复了

undo log和redo log的区别? (高频面试题)

  • redo log: 记录的是数据页的物理变化,服务宕机可用来同步数据
  • undo log :记录的是逻辑日志,当事务回滚时,通过逆操作恢复原来的数据
  • redo log保证了事务的持久性,undo log保证了事务的原子性和一致性

MVCC 是什么?

Multi-Version Concurrency Control,多版本并发控制。指维护一个数据的多个版本,使得读写操作没有冲突,是InnoDB实现高并发事务的核心机制,通过维护数据的多个版本,使得读操作无需加锁即可实现非阻塞的一致性读,同时保证事务的隔离性

MVCC的具体实现,主要依赖于数据库记录中的隐式字段、undo log日志、readView。


核心原理:

1 数据版本链

每行数据包含三个隐藏字段

  • DB_TRX_ID:最近修改该行的事务ID(6字节)。
  • DB_ROLL_PTR:指向Undo Log中历史版本的指针(7字节)。
  • DB_ROW_ID:隐含自增的行ID(若表无主键时生成,6字节)。
  1. ReadView(读视图):定义事务在某个时间点能看到哪些数据版本。是快照读 SQL执行时MVCC提取数据的依据

生成规则:

  • 读已提交(RC):每次SELECT生成新的ReadView。
  • 可重复读(RR):事务第一次SELECT生成ReadView,后续复用。

ReadView结构:

ReadView = {
    creator_trx_id,    // 当前事务ID
    m_ids,             // 生成ReadView时的活跃事务ID列表
    min_trx_id,        // 最小活跃事务ID
    max_trx_id         // 预分配的下一个事务ID
}

mysql 如何判断当前事务状态?

不同的隔离级别,生成ReadView的时机不同:

  • READ COMMITTED :在事务中每一次执行快照读时生成ReadView。
  • REPEATABLE READ:仅在事务中第一次执行快照读时生成ReadView,后续复用该ReadView。

例如:

  • 事务A(TRX_ID=100)查询时生成ReadView,活跃事务为[101, 102]。
  • 某行数据TRX_ID=99 → 可见(已提交)。
  • 某行数据TRX_ID=101 → 不可见(活跃中)。

什么是快照读和当前读?

当前读: 主要就是增删改操作

  • 读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。
  • 对于我们日常的操作,如:
  1. select ... lock in share mode(共享锁),
  2. select ... for update、update、insert、delete(排他锁)都是一种当前读

快照读: 主要就是查询操作

简单的select(不加锁)就是快照读,快照读,读取的是记录数据的可见版本,有可能是历史数据,不加锁,是非阻塞读。

  • Read Committed:每次select,都生成一个快照读。
  • Repeatable Read:开启事务后第一个select语句才是快照读的地方。

举例:

RC 级别下 Trx_id 初始为4 不符合右边的条件,mysql 就沿着undolog 往下走 到3 成功读取到数据

解析一下MVCC? (高频面试题)

MySQL中的多版本并发控制。指维护一个数据的多个版本,使得读写操作没有冲突
隐藏字段:
trx_id(事务id),记录每一次操作的事务id,是自增的
roll_pointer(回滚指针),指向上一个版本的事务版本记录地址
undo log:
回滚日志,存储老版本数据
版本链:多个事务并行操作某一行记录,记录不同事务修改数据的版本,通过roll_pointer指针形成一个链表
readView: 解决的是一个事务查询选择版本的问题
根据readView的匹配规则和当前的一些事务id判断该访问那个版本的数据
不同的隔离级别快照读是不一样的,最终的访问的结果不一样
RC :每一次执行快照读时生成ReadView
RR:仅在事务中第一次执行快照读时生成ReadView,后续复用

事务的隔离级别 (高频面试)

并发事务问题:

  • 脏读: 一个事务读到另外一个事务还没有提交的数据。
  • 不可重复读: 一个事务先后读取同一条记录,但两次读取的数据不同,称之为不可重复读
  • 幻读: 一个事务按照条件查询数据时,没有对应的数据行,但是在插入数据时,又发现这行数据已经存在,好像出现了”幻影”。

怎么解决上面这些事务问题了?

事务隔离级别越高,数据越安全,但是性能越低。

mysql 默认的隔离级别是RR (可重复读取) ,也就是mvcc中的readview 视图多个事务是可以共享的

ACID特性实现的原理?

  • 原子性:通过Undo Log记录事务前的数据版本和事务状态管理,确保操作要么全部成功,要么全部回滚。
  • 隔离性:基于MVCC和锁机制(行锁/间隙锁)控制并发事务间的可见性与互斥性。
  • 持久性:依赖Redo Log的预写日志和Double Write Buffer的崩溃恢复机制,确保提交后数据永久保存。
  • 一致性:通过数据库约束(唯一键、外键等)和原子性+隔离性+持久性的协同作用,保证数据始终合法有效。

好了,看完这篇文章,大家对于mysql的事务,是不是又有了新的认识了,

后面笔者将继续带大家学习更多的底层原理和常见面试题,欢迎大家留言讨论,评论,转发,收藏..

最近发表
标签列表