MySQL(十三):事务基础与事务操作

目录

一、为什么需要事务

[1. 经典转账案例分析](#1. 经典转账案例分析)

[2. 多用户并发访问与控制失效](#2. 多用户并发访问与控制失效)

二、事务的基本概念

[1. 事务的生命周期与状态演进](#1. 事务的生命周期与状态演进)

三、事务的四大特性(ACID)

[1. 原子性](#1. 原子性)

[2. 一致性](#2. 一致性)

[3. 隔离性](#3. 隔离性)

[4. 持久性](#4. 持久性)

[四、MySQL 事务支持](#四、MySQL 事务支持)

[1. 存储引擎的事务支持](#1. 存储引擎的事务支持)

[2. InnoDB 支持事务架构](#2. InnoDB 支持事务架构)

[3. MyISAM 不支持事务原因](#3. MyISAM 不支持事务原因)

五、事务提交方式

[1. 自动提交机制](#1. 自动提交机制)

[2. 查看与修改自动提交状态](#2. 查看与修改自动提交状态)

[3. 自动提交关闭后的底层行为](#3. 自动提交关闭后的底层行为)

六、事务基本操作

[1. 事务控制基本指令](#1. 事务控制基本指令)

[2. 控制流场景演示](#2. 控制流场景演示)

[2.1 数据流的正常提交](#2.1 数据流的正常提交)

[2.2 业务异常触发主动回滚](#2.2 业务异常触发主动回滚)

[2.3 客户端突发物理中断导致的隐式回滚](#2.3 客户端突发物理中断导致的隐式回滚)

[3. 事务操作注意事项](#3. 事务操作注意事项)

[3.1 警惕隐式提交风险](#3.1 警惕隐式提交风险)

[3.2 严格控制长事务的存活周期](#3.2 严格控制长事务的存活周期)

七、事务隔离性

[1. 隔离性与并发冲突的本质](#1. 隔离性与并发冲突的本质)

[2. 为什么需要不同的隔离级别](#2. 为什么需要不同的隔离级别)

[3. SQL 的四种隔离级别](#3. SQL 的四种隔离级别)

[4. 修改隔离级别](#4. 修改隔离级别)

总结


一、为什么需要事务

在数据库系统的实际应用中,数据的完整性与准确性是评估可靠性的核心指标。在面对高并发访问以及各类突发故障(如断电、网络中断)时,单纯依赖常规的增删改查操作无法保障数据安全

为此,关系型数据库引入了事务(Transaction)这一核心技术。本章将从事务的理论根基出发,逐步深入到 MySQL 的多引擎支持与事务基本操作规范


1. 经典转账案例分析

在金融或电商场景中,一笔业务往往由多个相互关联的数据库修改步骤组成。假设账户 A 向账户 B 转账 100 元,在数据库层面必须执行两条独立的 UPDATE 语句:

sql 复制代码
-- 步骤 1:账户 A 扣减 100 元
UPDATE account SET balance = balance - 100 WHERE name = 'A';

-- 步骤 2:账户 B 增加 100 元
UPDATE account SET balance = balance + 100 WHERE name = 'B';

若不加任何控制,在步骤 1 执行成功后,若服务器突然发生物理断电、网络中断或数据库进程被系统强行终止,步骤 2 将无法执行。此时,系统总资金将无故减少 100 元。这种由于多步操作未全部完成而导致的数据逻辑错误,即为典型的数据不一致性


2. 多用户并发访问与控制失效

数据库作为多线程共享资源,在线上环境中经常面临数万个连接同时对同一张表发起 CRUD 操作。如果系统缺乏统一的调度与控制,并发操作将引发严重的逻辑冲突

例如,两个线程同时读取到同一件商品的库存为 1。随后,两个线程分别对其执行扣减库存的操作。由于彼此缺乏感知,最终库存会被修改为 0,导致原本只能售出一件的商品被超卖。并发访问的冲突直接威胁着数据库的数据安全

数据库的解决方案

为了消除上述安全隐患,数据库必须提供一种机制:将多个独立的 SQL 操作打包成一个不可分割的逻辑执行整体。这个整体要么全部执行成功并持久化到磁盘,要么在任何一个环节出错时全部撤销,使数据恢复到初始状态。这一机制正是事务存在的根本价值

二、事务的基本概念

事务是数据库管理系统执行过程中的一个逻辑单位,它由一条或多条紧密相关的 SQL 语句(通常为 INSERT、UPDATE、DELETE 等 DML 语句)组合而成。事务是数据库进行逻辑控制与资源分配的最小物理单元


1. 事务的生命周期与状态演进

在事务的整个执行过程中,其内部状态会随着 SQL 的执行和系统指令的下达而发生动态转移:

  • 活动状态(Active):事务显式开启后,正在执行一系列 SQL 语句时的状态

  • 部分提交状态(Partially Committed):事务中的最后一条 SQL 语句已执行完毕,但由于数据目前仅存在于内存缓冲池中,尚未完全固化到磁盘物理文件

  • 失败状态(Failed):在活动状态或部分提交状态下,由于系统硬件故障、SQL 语法错误或死锁冲突,导致事务无法继续正常运行

  • 中止状态(Aborted):当事务进入失败状态后,数据库执行引擎会调用回滚操作,将已经修改的数据撤销。此时数据库恢复到事务启动前的状态

  • 提交状态(Committed):数据被安全地写入到物理磁盘文件,事务宣告成功结束

三、事务的四大特性(ACID)

事务之所以能够提供数据一致性保障,完全依赖于其严格遵循的四大物理特性,即 ACID 特性

特性名称 核心物理定义与职责
A - 原子性(Atomicity) 事务是不可分割的最小物理工作单元。事务中的所有操作要么全部成功,要么全部不执行。一旦中间某个步骤失败,整个事务必须被强制回滚到初始状态,不允许停留在中间任何一个非稳态
C - 一致性(Consistency) 事务执行的前后,数据库的完整性约束和业务逻辑状态必须保持合法。一致性是事务的最终追求目标,原子性、隔离性、持久性在底层均是为保障一致性而服务的
I - 隔离性(Isolation) 当多个事务并发执行时,各个事务之间应当是相互隔离、互不干扰的。一个事务在未提交前,其内部的中间数据改动原则上不应影响其他正在并发运行的事务
D - 持久性(Durability) 一旦事务被提交,它对数据库中数据的修改就是永久性的。接下来即使遭遇严重的物理断电或系统崩溃,数据库重启后也必须能够通过日志恢复找回已提交的数据

为了彻底理解 ACID 的每一项特性如何体现,我们首先定义一个贯穿本章的转账业务沙盒

设有银行账户表 bank_account,包含两个账户:账户 A 和账户 B。初始状态下,两个账户的资金配置如下:

账户名称 (name) 账户余额 (balance) 约束条件
A 1000 元 balance >= 0 (非负约束)
B 1000 元 balance >= 0 (非负约束)

现在有一个转账场景:

sql 复制代码
-- 步骤 1:账户 A 扣减 100 元
UPDATE bank_account SET balance = balance - 100 WHERE name = 'A'; 
-- 步骤 2:账户 B 增加 100 元
UPDATE bank_account SET balance = balance + 100 WHERE name = 'B';

1. 原子性

为什么需要原子性

在没有原子性保护的系统中,代码和硬件的执行是分步进行的。在上述转账案例中,如果将 A 的余额变为 900 元后,数据库服务器遭遇了物理断电操作系统强制杀掉进程,导致步骤 2 永远没有机会执行。此时,100 元凭空消失

原子性究竟体现在哪里

原子性的核心定义是:一个事务内部的所有操作,在物理磁盘和逻辑状态上,被视为一个不可分割的最小单元

  • 成功场景的体现:步骤 1 与 步骤 2 全部顺利执行完毕,数据由内存缓冲池安全刷新至磁盘,A 变 900 元,B 变 1100 元

  • 失败场景的体现 :如果步骤 1 执行成功,而在步骤 2 执行前系统发生崩溃,在系统重启恢复时,数据库不会 允许 A 停留在 900 元这个中间状态。存储引擎会自动读取 Undo Log(回滚日志),将 A 的余额由 900 元强行改回初始的 1000 元

  • 物理效果:对外界而言,这个事务要么如同从未发生过(全回滚),要么完美结束(全提交),绝不存在 "只执行了一半" 的中间状态


2. 一致性

为什么需要一致性

数据库不仅仅是存放字节的物理容器,它还承载着上层业务的合法逻辑与物理完整性约束。如果 A 转账给 B 的过程中,转账金额写成了 -100 元,或者由于代码逻辑错误导致 A 扣了 100 元,B 却加了 500 元,这都会破坏系统的完整性

一致性究竟体现在哪里

一致性是事务追求的核心目标 。它的物理定义是:事务的执行,必须使数据库从一个合法的合法状态,转移到另一个合法的合法状态

  • 物理层面的体现:在转账前后,数据库自带的硬性约束不能被破坏。例如,如果 balance 字段设置了无符号约束,任何导致余额变为负数的事务都会被系统强制拒绝

  • 业务层体现 :转账前总额是 1000 + 1000 = 2000 元;转账后,无论是事务成功后的 900 + 1100 = 2000 元,还是事务失败回滚后的 1000 + 1000 = 2000 元,总和始终是 2000 元。数据在业务逻辑链条上没有逻辑漏洞,这就是一致性的物理体现


3. 隔离性

为什么需要隔离性

现代数据库是高并发运行的,同一时刻可能有成百上千个事务在操作同一张表。假设在上述转账事务执行到中途(即 A 已经扣了 100 元,但 B 还没来得及加 100 元时),另一个独立的财务报表统计事务突然发起了一次全表查询:SELECT SUM(balance) FROM bank_account

如果没有隔离性控制,事务 Y 将读取到 A 的余额为 900 元,B 的余额为 1000 元,统计出的系统总额为 1900 元。这直接导致财务报表产生了严重的数据畸变

隔离性究竟体现在哪里

隔离性的物理定义是:多个并发执行的事务之间,其内部的操作和中间状态应当是隔离的,并发事务的执行互不干扰

  • 物理层面的体现 :为了实现这种隔离,InnoDB 引入了锁机制与 MVCC(多版本并发控制)

  • 业务场景的体现 :在事务 X 尚未提交之前,它对 A 和 B 的余额所做的任何修改,对并发的事务 Y 而言都是隐形的。事务 Y 发起查询时,数据库会构建出一个快照版(Read View),让事务 Y 看到的 A 和 B 依然是最初的 1000 元。直到事务 X 正式提交后,其修改结果才会对其他事务可见。各个并发事务的运行互不交叉


4. 持久性

为什么需要持久性

为了追求极致的读写性能,现代数据库的修改操作首先是在内存的缓冲池中进行的。如果一个事务在内存中修改完数据,并且向客户端返回了确认信号,但此时这些变动的页面还没有来得及刷入硬盘。 如果在这一瞬间服务器突然断电,内存中的数据将全部丢失。如果重启后数据无法恢复,这就意味着数据库失信于用户,属于严重的生产事故

持久性究竟体现在哪里

持久性的物理定义是:一个事务一旦被显式提交,它对数据的修改就必须永久固化到非易失性的物理存储介质中。接下来即使发生任何断电或宕机,数据也绝不丢失

  • 物理层面的体现 :InnoDB 存储引擎通过 Write-Ahead Logging(日志先行技术) 来保障持久性。在事务提交时,虽然 16KB 的数据页还在内存中没刷盘,但系统会强制要求将记录本次修改物理行字节变动的 Redo Log 实时刷新到磁盘文件中

  • 崩溃恢复:由于 Redo Log 是物理顺序追加写入的,速度极快。一旦服务器断电重启,InnoDB 的启动例程会自动扫描磁盘上的 Redo Log,把断电前已经提交、但在内存中尚未刷新到数据文件里的改动,强行在磁盘数据页上重新模拟运行一遍。确保事务一旦确认提交,其执行结果便拥有了不可动摇的永久物理效力

四、MySQL 事务支持

在 MySQL 的存储引擎架构中,不同的存储引擎对事务的支持能力存在差异。在实际工程中,理解这些引擎在物理层面的差异,是正确进行数据库选型和性能调优的基础


1. 存储引擎的事务支持

MySQL 支持多种存储引擎,但针对业务核心的交易型数据,支持事务的引擎与不支持事务的引擎有着严格的界限:

  • 支持事务的引擎:InnoDB、NDB(集群存储引擎)。其中 InnoDB 是自 MySQL 5.5 版本以来的默认存储引擎,也是生产环境中应用最广泛的事务型存储引擎

  • 不支持事务的引擎:MyISAM、MEMORY、ARCHIVE。这些引擎在处理数据变更时,不具备原子性回滚和崩溃恢复的能力


2. InnoDB 支持事务架构

InnoDB 能够完美提供 ACID 特性,并非仅仅在软件逻辑层做了限制,而是其底层物理架构中设计了专用的日志系统与内存控制机制

日志系统的协同(Redo Log 与 Undo Log)

InnoDB 的底层包含两个互补的物理日志系统:

  • Undo Log(回滚日志) :负责保障原子性(A)。每当事务执行一条 DML 语句(如 INSERT 或 UPDATE)时,InnoDB 都会在 Undo Log 中记录对应的逆向操作。如果事务因异常需要回滚,系统会执行这些逆向操作将数据恢复原状

  • Redo Log(重做日志) :负责保障持久性(D)。为了解决内存缓冲池与磁盘异步刷盘之间的时间差,InnoDB 采用 WAL 技术。事务提交时,只需将变更记录顺序写入 Redo Log 磁盘文件即可宣告成功。断电重启后,系统会通过重做日志的内容进行前滚恢复

锁机制与多版本并发控制(MVCC)

为了保障隔离性(I),InnoDB 实现了行级锁与多版本并发控制(MVCC)。行级锁可以最大程度减少并发事务操作同一张表时的相互阻塞;而 MVCC 则允许读写操作并发执行,读取操作不需要等待写入锁的释放,通过读取数据的历史快照版本,实现了高效的弱隔离或强隔离环境


3. MyISAM 不支持事务原因

与 InnoDB 相比,MyISAM 存储引擎在架构设计上追求的是极简的读取速度和较低的存储开销,因此它完全舍弃了事务控制组件

缺乏事务日志缓冲

MyISAM 引擎没有类似于 InnoDB 的 Redo Log 和 Undo Log 结构

  • 当对 MyISAM 表执行批量更新(例如一条 SQL 语句修改 1000 行数据)时,修改会直接作用于磁盘的 .MYD(数据文件)和 .MYI(索引文件)

  • 如果在修改到第 500 行时服务器突然断电或程序报错,由于没有 Undo Log 记录历史版本,MyISAM 无法执行任何撤销操作。这导致前 500 行修改已经生效,后 500 行未修改,整张表的数据处于逻辑破碎的状态

锁机制

MyISAM 仅支持表级锁(Table-level Locking)。当一个写操作触发时,整张表都会被锁住,其他所有的读写操作必须完全排队等待。这种粗粒度的锁机制导致它无法处理高并发下的多事务逻辑交叉,因此也无从实现复杂的事务隔离级别

底层架构对比

为了直观呈现两种引擎在事务支持层面的差异,其对比关系如下:

特性 InnoDB 存储引擎 MyISAM 存储引擎
事务支持 支持 不支持
锁颗粒度 支持行级锁(并发度高) 仅支持表级锁(并发度低)
原子性支持 依赖 Undo Log 实现物理/逻辑回滚 无回滚日志,发生错误无法撤销
持久性支持 依赖 Redo Log 与 WAL 技术保障 直接写入磁盘,断电易损坏数据
崩溃恢复能力 具备自动 Crash Recovery 能力 崩溃后需要手动 REPAIR TABLE,易丢失数据
适用场景 高并发、核心高频交易、数据一致性要求极高的业务 读多写少、以报表分析或归档为主的非核心业务

五、事务提交方式

MySQL 的 InnoDB 存储引擎在处理事务时,具备两种截然不同的事务提交行为模式:自动提交(Auto-commit)手动提交(Manual-commit)。这两种模式直接决定了单条 SQL 语句在底层的边界归属和持久化时机


1. 自动提交机制

在默认配置下,MySQL 开启了全局的自动提交机制

物理逻辑

当 autocommit 功能开启时,用户向数据库发送的每一条独立的 DML 语句(如 INSERT、UPDATE、DELETE),都会被系统隐式地包装成一个独立的、生命周期极短的事务

  • 语句开始执行时,系统自动隐式开启事务

  • 语句执行成功后,系统立即在后台隐式触发 COMMIT 指令,将修改固化到磁盘中

  • 如果语句执行失败,系统则自动触发 ROLLBACK

这种模式简化了非交易型业务的代码开发。但对于需要多条 SQL 协同的复合业务(如前文所述的转账场景),默认的自动提交模式会导致操作步骤被强行拆散,从而破坏原子性


2. 查看与修改自动提交状态

数据库管理员和开发人员可以通过系统变量对当前会话或全局的提交行为进行精准控制

查看自动提交状态

使用系统变量查询指令,可以检索当前的自动提交配置:

sql 复制代码
SHOW VARIABLES LIKE 'autocommit';

若 Value 为 ON(或数字 1),代表当前处于自动提交模式;若为 OFF(或数字 0),则代表自动提交已关闭

修改自动提交方式

通过 SET 可以调整该状态位:

sql 复制代码
-- 关闭当前会话的自动提交(切换为手动提交模式)
SET autocommit = 0;

-- 开启当前会话的自动提交
SET autocommit = 1;

3. 自动提交关闭后的底层行为

当执行 SET autocommit = 0 显式关闭自动提交后,会话的底层数据写入流程会发生本质变化:

  1. 隐式事务开启:用户输入第一条 DML 语句(例如 UPDATE)时,InnoDB 会在后台静默开启一个新事务,并将受影响的数据行进行加锁控制

  2. 状态持续挂起:随后无论输入多少条增删改语句,这些修改都只停留在内存缓冲池和事务的私有空间中,对其他并发会话不可见,也没有真正固化。此时事务处于 "活动" 状态

  3. 必须显式提交:该事务的生命周期不会随着单条 SQL 语句的结束而终结。必须由客户端显式发送 COMMIT 指令,数据才会被写入磁盘并释放行锁;或者发送 ROLLBACK 指令,撤销此期间的所有改动

资源开销:在 autocommit = 0 的手动模式下,如果开发人员在执行完操作后忘记书写 COMMIT,该事务将一直保持活跃状态。这会导致持有的行锁无法释放,长时间占据内存中的 Undo Log 资源,进而引发高并发环境下的锁等待超时或阻塞风险

六、事务基本操作

在明确了事务的理论逻辑与提交机制后,本节将聚焦于开发层面的具体操作。我们将通过构建一个标准的工程沙盒环境,演示在手动控制模式下,事务的开启、提交、回滚以及异常中断时的底层流转行为

准备测试表

为了确保演示结果的确定性,首先创建一张标准的银行账户余额表并写入初始测试数据

sql 复制代码
-- 创建测试表
CREATE TABLE t_balance (
    account_id INT PRIMARY KEY,
    user_name VARCHAR(50) NOT NULL,
    balance DECIMAL(10, 2) NOT NULL
) ENGINE=InnoDB;

-- 插入初始实验数据
INSERT INTO t_balance (account_id, user_name, balance) VALUES 
(1, '张三', 1000.00),
(2, '李四', 1000.00);

1. 事务控制基本指令

在 MySQL 中,显式控制一个事务的边界主要依赖以下三条核心指令:

  • START TRANSACTION 或 BEGIN: 显式开启一个新事务。执行该指令后,当前会话将暂时挂起自动提交(autocommit)机制,后续执行的 DML 语句都会被归入当前事务的范围中,直到遇到明确的终结指令

  • COMMIT: 显式提交事务。将当前事务中所有修改操作引发的"脏页"变动逻辑持久化到磁盘上,并释放该事务在运行期间持有的行级锁。执行后,事务生命周期结束

  • ROLLBACK: 显式回滚事务。调用存储引擎的逆向日志(Undo Log),撤销自 START TRANSACTION 以来当前事务执行的所有 DML 数据变动,将表结构中的数据恢复至事务开启前的最初状态,并释放锁


2. 控制流场景演示

下面分别模拟三种在生产环境中最为常见的事务控制流走向。

2.1 数据流的正常提交

模拟张三向李四成功转账 200 元的完整流程:

sql 复制代码
-- 步骤 1:显式声明事务开始
START TRANSACTION;

-- 步骤 2:执行扣款与存款更新
UPDATE t_balance SET balance = balance - 200 WHERE user_name = '张三';
UPDATE t_balance SET balance = balance + 200 WHERE user_name = '李四';

-- 步骤 3:在另一个并发会话中查询,此时两者的余额依然是 1000.00(隔离性体现)

-- 步骤 4:确认业务无误,显式提交
COMMIT;

终端1:

终端2:

提交后,再次查询表数据,张三余额变为 800.00,李四余额变为 1200.00。数据被物理固化

2.2 业务异常触发主动回滚

模拟在执行更新过程中发现金额配置错误,需要全额撤销的操作:

sql 复制代码
-- 步骤 1:显式声明事务开始
START TRANSACTION;

-- 步骤 2:错误地将张三的余额扣减了 5000 元(实际上张三只有 1000 元)
UPDATE t_balance SET balance = balance - 5000 WHERE user_name = '张三';

-- 步骤 3:业务逻辑检查发现余额即将变成负数,触发异常处理,执行回滚
ROLLBACK;

执行 ROLLBACK 后,查询表数据,张三的余额依然保持在标准的 800.00。上一步错误的 UPDATE 修改被存储引擎完全抹去,未对物理数据造成任何污染

2.3 客户端突发物理中断导致的隐式回滚

模拟在分布式系统或网络通信中,客户端与数据库失去连接的极端情况:

sql 复制代码
START TRANSACTION;
UPDATE t_balance SET balance = balance - 100 WHERE user_name = '张三';
-- 注意:此时未输入 COMMIT,也未输入 ROLLBACK

此时,模拟客户端突然遭遇断网、崩溃,或者直接强行关闭了当前的连接终端(杀死进程)

MySQL 服務器端的连接线程池会检测到该客户端会话(Session)的异常中断。为了保障数据的绝对一致,InnoDB 引擎会强行接管该孤儿事务,并在后台自动引发隐式回滚(Implicit Rollback),撤销该事务尚未提交的修改,同时将其占用的行锁资源安全释放


3. 事务操作注意事项

在实际的后端开发与 SQL 编写中,须严格遵守以下两条防范准则:

3.1 警惕隐式提交风险

在一个通过 START TRANSACTION 显式开启的事务中,如果其中夹杂了任何 DDL 语句 (如 CREATE TABLE、ALTER TABLE、DROP TABLE、TRUNCATE TABLE 等),MySQL 内部的优化器会立刻强制执行一次隐式提交

sql 复制代码
START TRANSACTION;
UPDATE t_balance SET balance = balance - 100 WHERE user_name = '张三';

-- 在此处执行了结构调整
ALTER TABLE t_balance ADD COLUMN remark VARCHAR(255); 
-- 警告:此时前面的 UPDATE 语句已经被 MySQL 强制 COMMIT 了!

ROLLBACK; -- 此时执行回滚,由于事务已被强制隐式终结,回滚失效

3.2 严格控制长事务的存活周期

长事务是指在启动后,长时间不执行 COMMIT 或 ROLLBACK 的事务。长事务会带来严重的工程灾难:

  • 锁等待蔓延:事务存活期间持有的行锁无法释放,会导致高并发下其他线程批量处于 Lock wait timeout 状态

  • Undo Log 膨胀:为了维持该事务的隔离性快照,InnoDB 无法清理相关的 Undo Log 版本链,导致数据库的系统表空间(.ibd)急剧膨胀,拖慢整体磁盘读写吞吐量

因此,在业务设计上,应当尽量避免在事务内部封装耗时较长的 RPC 远程网络调用或高复杂的计算逻辑

七、事务隔离性

在关系型数据库中,隔离性是处理高并发核心业务时最复杂的特性。如果数据库系统只服务于单个用户,或者所有的事务都严格按照先后顺序执行,那么数据安全将得到绝对保障

然而,在追求极致吞吐量的生产环境中,数万个事务同时对数据库发起读写请求是家常便饭。如何在隔离程度运行效率之间完美的平衡,正是事务隔离级别设计的核心逻辑


1. 隔离性与并发冲突的本质

隔离性规定了一个事务在进行数据修改时,其操作的中间状态在何时、以何种方式对其他并发事务可见

当多个事务并发访问共享的数据资源(如同一行记录、同一个数据页)时,根据它们各自的读写意图,会交织产生以下三种底层的物理碰撞场景:

  • 读-读(Read-Read):多个事务同时读取同一行数据。这种场景不会对数据造成任何物理破坏,完全不需要进行隔离性控制

  • 写-写(Write-Write):多个事务同时尝试修改同一行数据。这会导致 "更新丢失" 等严重物理冲突,数据库底层必须通过加锁机制强制让它们排队执行

  • 读-写(Read-Write)或 写-读(Write-Read) :一个事务在尝试修改某行数据,而另一个事务同时在读取这行数据。这是高并发环境下最普遍、最密集的碰撞场景。隔离级别的诞生,本质上就是为了约束和规范读-写并发时的底层可见性行为


2. 为什么需要不同的隔离级别

既然一致性那么重要,为什么不直接把隔离性做到极致,让所有并发事务都彻底隔离开来?

答案是为了性能

如果数据库将隔离性提升到最高规格(即任何事务都互不干扰、完全封闭),其底层的物理实现就不得不演变为串行排队。此时,上一个事务不结束,下一个事务就只能卡死等待。在高并发的电商或金融系统中,这会导致系统吞吐率断崖式下跌,响应时间急剧飙升

因此,SQL 标准精妙地引入了四个弹性的事务隔离级别

  • 隔离级别越低,系统并发性能越强,但数据安全性越低

  • 隔离级别越高,数据安全性越高,但系统的并发吞吐量越低

通过提供不同的隔离级别,数据库将选择权交给了开发人员,根据具体的业务场景自行做出最适合的性能平衡


3. SQL 的四种隔离级别

根据隔离程度从低到高,标准事务隔离级别被严格划分为以下四档:

读未提交

  • 核心逻辑 :这是隔离级别中的 "无防备" 档位。一个事务在运行期间,允许读取到其他并发事务尚未提交的修改数据

  • 并发性能:极致。因为读操作几乎不需要受到任何物理锁的束缚

  • 生产评价 :极度危险。由于它允许读取到那些极有可能被撤销的垃圾中间脏数据,在绝大多数企业级生产环境中都被明确禁止使用

读已提交

  • 核心逻辑:一个事务只能读取到其他并发事务已经正式提交之后的数据。只要对方事务还没有发出提交指令,它所做的任何改动对当前事务都是完全隐形的

  • 并发性能:良好。实现了基础的安全读写分离

  • 生产评价:非常实用。它是主流数据库的默认事务隔离级别,契合大多数常规业务

可重复读

  • 核心逻辑 :它确保在同一个事务的整个生命周期内,无论何时发起对同一行数据的读取,其看到的数据内容都是绝对一模一样、前后恒定的。即使在这期间有其他事务强行修改了这行数据并进行了提交,当前事务也绝不受其干扰

  • 并发性能:中等

  • 生产评价MySQL InnoDB 存储引擎的默认隔离级别。InnoDB 在该级别下通过 MVCC 和间隙锁技术,在维持了高并发的同时,提供了极强的一致性保障

串行化

  • 核心逻辑 :隔离级别的最高档位。它从根本上摒弃了读写的并发可能性,强制所有并发事务在底层完全转为单线程排队串行执行

  • 并发性能:级低。系统吞吐量极低

  • 生产评价:通常仅在极少数对数据准确性有极致要求、且并发量极低的特殊清算盘点业务中才会短暂开启

隔离级别 读写可见性 数据安全性 系统并发吞吐量 主流数据库默认
Read Uncommitted 能够读取到并发事务未提交的中间变动 极低 极致
Read Committed 只能读取到并发事务已提交的稳态数据 较低 优秀 Oracle / PostgreSQL
Repeatable Read 整个事务生存期内,同一行数据的读结果恒定 较高 良好 MySQL (InnoDB)
Serializable 事务之间完全通过加锁实现串行排队 极致 极低

4. 修改隔离级别

在日常开发与线上数据库调优中,掌握隔离级别的探查与切换指令是工程师的必备功底

查看当前环境的隔离级别

在 MySQL 8.0 及以上版本中,通过系统变量检索指令来确认当前的配置状态:

sql 复制代码
-- 方式一:查看当前当前会话(Session)的事务隔离级别
SELECT @@transaction_isolation;

-- 方式二:查看全局(Global)系统的事务隔离级别
SELECT @@global.transaction_isolation;

如果使用的是较老旧的 MySQL 5.7 或更早版本,上述变量名称为 tx_isolation,对应的查询命令应调整为 SELECT @@tx_isolation

修改事务隔离级别

MySQL 允许你在不同的作用域(全局级别、单会话级别、单事务级别)动态调整隔离:

sql 复制代码
-- 场景 A:仅修改当前连接会话的隔离级别(对其他并行的业务连接不造成任何影响,推荐调优测试使用)
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

-- 场景 B:修改全局的默认隔离级别(仅对后续新创建的数据库连接生效,需要具备管理员特权)
SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;

-- 场景 C:仅针对紧随其后的下一个即刻开启的事务进行单次微调
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

总结

综上所述,我们学习了 MySQL 事务的基本概念,理解了事务产生的原因以及 ACID 四大特性,并通过实际案例掌握了事务的开启、提交、回滚等常见操作。同时,我们还认识了事务隔离性的意义以及 MySQL 提供的四种事务隔离级别,为后续学习并发控制打下了基础。

不过,这些内容仍然停留在事务的使用层面。新的问题也随之而来:

事务为什么能够回滚?数据库断电后为什么还能恢复数据?不同隔离级别又是如何实现的?

这些问题都已经涉及事务的底层实现原理

因此,在下一篇中,我们将正式进入 MySQL 事务原理部分,从 Undo Log、Redo Log、MVCC 以及 Read View 等核心机制出发,深入理解事务能够保证 ACID 特性的真正原因