数据库事务

一、事务的核心定义

事务是一组不可分割的数据库操作集合,是数据库执行的最小逻辑工作单元。事务内的所有操作,要么全部执行成功,要么全部执行失败回滚,不存在 "部分成功、部分失败" 的中间状态。

经典场景示例

最典型的转账场景:A 账户给 B 账户转账 100 元,需要执行两个核心操作:

  1. A 账户余额扣减 100 元
  2. B 账户余额增加 100 元

如果没有事务,可能出现 "扣减成功、增加失败" 的情况,导致资金错乱;而事务会将这两个操作打包,保证要么全部生效,要么全部回滚,从根本上避免数据逻辑错误。

二、事务的四大特性(ACID)及底层实现

ACID 是事务的四大核心特性,也是事务的灵魂。其中:原子性是基础,隔离性是并发保障,持久性是可靠性底线,一致性是最终目的,原子性、隔离性、持久性均为保证一致性服务。

原子性(Atomicity)

原子性指事务是一个不可分割的最小执行单元,事务内的所有操作,要么全部执行成功并提交,要么全部执行失败并回滚到事务开始前的状态,不存在任何中间状态。

核心价值

彻底解决 "操作中途异常,数据半改半不改" 的问题,保证操作的完整性。比如转账操作中途数据库宕机,重启后不会出现 "扣款成功、到账失败" 的资金错乱。

底层实现原理:undo log(回滚日志)

InnoDB 完全通过 undo log(回滚日志) 实现事务的原子性,核心逻辑如下:

  1. undo log 本质 :undo log 是一种逻辑日志 ,记录的是数据修改的反向操作
    • 执行 INSERT 语句时,undo log 会记录对应的 DELETE 语句;
    • 执行 UPDATE 语句时,undo log 会记录将数据还原为修改前状态的反向更新语句;
    • 执行 DELETE 语句时,undo log 会记录对应的 INSERT 语句。
  2. 写入时机:事务执行过程中,每修改一行数据,都会先将对应的反向操作写入 undo log,并持久化到磁盘,再执行数据修改。
  3. 回滚逻辑 :当事务执行失败、手动执行 ROLLBACK,或数据库宕机重启时,InnoDB 会读取 undo log 中的反向操作,逐行将数据恢复到事务开始之前的状态,撤销所有未提交的修改,保证所有操作要么全成、要么全败。
  4. 补充特性:undo log 不仅用于实现原子性,也是 InnoDB MVCC(多版本并发控制)的核心基础,用于实现事务的隔离性。

2.2 一致性(Consistency)

一致性指事务执行前后,数据库的完整性约束不会被破坏,数据始终符合业务逻辑的预期。完整性约束分为两个层面:

  • 数据库层面约束:主键不重复、唯一索引约束、外键关联约束、非空约束、字段类型约束等;
  • 业务层面约束:转账前后两个账户的总余额不变、商品库存不能为负数、用户积分不能为负等符合业务规则的逻辑约束。
核心价值

一致性是事务的最终目标,原子性、隔离性、持久性都是手段,最终都是为了保证数据始终符合预期,不会出现逻辑错乱。

底层实现原理

一致性无法完全由数据库独立实现,需要数据库层面保障 + 业务层面保障共同完成:

  1. 数据库层面的一致性保障
    • 依托原子性、隔离性、持久性的底层机制,保证事务不会出现中间状态、不会被其他并发事务干扰、不会丢失数据,从根本上避免数据错乱;
    • 内置约束校验:主键、唯一索引、外键、非空等约束,在数据写入时自动校验,不符合约束的操作会直接报错拒绝,保证数据的物理完整性。
  2. 业务层面的一致性保障
    • 数据库无法校验业务逻辑的合理性,比如转账时 "扣减 100 元、增加 200 元" 的 SQL,数据库不会拦截,这部分必须由开发人员在业务代码中保证;
    • 业务层面需要通过合理的 SQL 编写、参数校验、异常处理,保证事务内的操作符合业务规则,最终实现数据的逻辑一致性。

2.3 隔离性(Isolation)

核心定义

隔离性指多个并发执行的事务之间,是相互隔离、互不干扰的,一个事务的内部操作和中间状态,对其他事务不可见,每个事务都感觉不到其他事务在并发执行。

核心价值

解决多事务并发读写同一行数据时,出现的数据安全问题,在并发性能和数据安全之间做平衡。

无隔离性会出现的 3 类并发问题

表格

问题类型 核心定义 场景示例
脏读 一个事务读取到了另一个事务未提交的修改数据 事务 A 修改了用户余额,未提交;事务 B 读取到了这个修改后的值;随后事务 A 回滚,事务 B 读到的就是无效的脏数据
不可重复读 一个事务内,两次读取同一行数据,结果不一致 事务 A 第一次读取用户余额为 1000 元;期间事务 B 修改了该余额为 800 元并提交;事务 A 第二次读取,余额变成了 800 元,同一事务内两次读取结果不一致
幻读 一个事务内,两次用相同条件查询,返回的行数不一致 事务 A 用WHERE id>10查询到 5 条数据;期间事务 B 插入了一条id=15的数据并提交;事务 A 再次用相同条件查询,得到 6 条数据,仿佛出现了 "幻觉"
SQL 标准的 4 个隔离级别

SQL 标准定义了 4 个从低到高的隔离级别,级别越高,数据安全性越强,并发性能越差。InnoDB 的默认隔离级别为可重复读(RR)

表格

隔离级别 英文全称 解决的问题 存在的问题 适用场景
读未提交 Read Uncommitted(RU) 脏读、不可重复读、幻读 几乎无生产使用场景
读已提交 Read Committed(RC) 脏读 不可重复读、幻读 Oracle、SQL Server 默认级别,适合对实时性要求高的场景
可重复读 Repeatable Read(RR) 脏读、不可重复读 理论上存在幻读,InnoDB 通过临键锁解决了幻读 InnoDB 默认级别,生产环境通用
串行化 Serializable 脏读、不可重复读、幻读 完全串行执行,并发性能极差 仅对数据安全性要求极高、无并发的场景
底层实现原理:MVCC + 锁机制

隔离性的底层实现分为两大核心:MVCC(多版本并发控制) 实现读写不阻塞,提升并发性能;锁机制 实现写写互斥,保证数据安全。

1. 锁机制

InnoDB 提供了多粒度的锁,核心分类如下:

  • 按锁的功能划分
    • 共享锁(S 锁 / 读锁):多个事务可以同时对同一行数据加 S 锁,加锁后只能读不能修改;
    • 排他锁(X 锁 / 写锁):只有一个事务能对同一行数据加 X 锁,加锁后其他事务不能加任何锁,也无法修改、读取该行数据。
  • 按锁的粒度划分
    • 行级锁:仅锁定目标行数据,锁粒度最细,并发性能最高,InnoDB 核心使用;
    • 表级锁:锁定整个表,锁粒度最粗,并发性能极差,仅特殊场景使用;
    • 临键锁(Next-Key Lock):InnoDB RR 级别下的默认行锁算法,是「行锁 + 间隙锁」的组合,锁定一个左开右闭的区间,不仅锁定已存在的数据行,还锁定区间内的间隙,防止其他事务插入符合条件的数据,从根本上解决了幻读问题。
2. MVCC(多版本并发控制)

MVCC 是 InnoDB 实现隔离性的核心,核心价值是不加锁就能实现读写不阻塞,大幅提升数据库的并发性能,仅在 RC 和 RR 两个隔离级别下生效。

  • 实现基础

    1. 每行数据的隐藏列:InnoDB 会为每一行数据添加 3 个隐藏列,核心两个为:
      • DB_TRX_ID:最后一次修改该行数据的事务 ID;
      • DB_ROLL_PTR:回滚指针,指向该行数据在 undo log 中的历史版本。
    2. undo log 历史版本链:每次修改数据,都会在 undo log 中记录修改前的版本,通过回滚指针形成一条完整的版本链。
    3. Read View(读视图):事务执行 SELECT 时生成的一致性读视图,用于判断数据的哪个版本对当前事务可见。
  • 核心工作逻辑事务执行 SELECT 时,会生成 Read View,根据规则判断版本链中的数据是否可见,只读取符合可见规则的版本,无需加锁,也不会被写操作阻塞。

  • RC 与 RR 隔离级别的核心差异两个隔离级别的核心区别,在于 Read View 的生成时机:

    • RC 级别 :事务内每次执行 SELECT 都会生成一个全新的 Read View,因此能读到其他事务已经提交的最新数据,所以会出现不可重复读;
    • RR 级别 :事务内只有第一次执行 SELECT 时生成 Read View,后续所有 SELECT 都复用这个视图,因此同一个事务内读到的数据版本始终一致,实现了可重复读。

2.4 持久性(Durability)

核心定义

持久性指一个事务一旦提交成功,它对数据库的修改就会永久保存到磁盘中,后续的任何操作、服务器宕机、断电、系统崩溃,都不会导致这次提交的修改丢失。

核心价值

是数据库数据可靠性的底线,保证已提交的数据不会因为系统故障而丢失。

底层实现原理:redo log + WAL 预写日志 + 两阶段提交
1. 核心痛点:直接刷磁盘的性能问题

InnoDB 的数据存储在磁盘的「数据页」中(默认每页 16KB),如果每次修改数据都直接把数据页刷到磁盘,属于随机 IO,性能极差,完全无法支撑高并发场景。

为了解决这个问题,InnoDB 采用了 WAL(Write-Ahead Logging,预写日志) 机制,核心逻辑是:先写日志,再写内存,最后异步刷磁盘。只要日志写入磁盘成功,事务就算提交成功,就算数据库宕机,重启后也能通过日志恢复数据,保证持久性。

2. redo log(重做日志)

redo log 是 WAL 机制的核心,也是实现持久性的基础:

  • 本质:物理日志,记录的是「某个数据页在某个偏移量做了什么修改」,不是 SQL 逻辑,宕机恢复速度极快;
  • 写入特性:循环写入,固定大小配置(默认 4 个文件,每个 1GB,总计 4GB),写满后会将日志对应的修改刷到磁盘的数据页中,再循环复用;
  • 归属:InnoDB 引擎特有,仅用于 InnoDB 数据的崩溃恢复。
3. 关键刷盘策略

InnoDB 通过 innodb_flush_log_at_trx_commit 参数控制 redo log 的刷盘时机,直接决定了持久性和性能的平衡:

  • 值为 1(默认值):每次事务提交,都会将 redo log 从内存刷到磁盘并持久化。宕机不会丢失任何已提交的数据,安全性最高,生产环境必须使用该配置;
  • 值为 0:每秒将 redo log 刷到磁盘一次,事务提交时不刷盘。数据库宕机最多丢失 1 秒的数据,性能最高,安全性极低,生产环境禁止使用;
  • 值为 2:事务提交时,将 redo log 写入操作系统缓存,每秒刷到磁盘一次。数据库宕机不会丢失数据,服务器宕机最多丢失 1 秒的数据,性能优于 1,安全性低于 1,非核心场景可使用。
4. 两阶段提交(2PC)

MySQL 分为 Server 层和引擎层,Server 层有 binlog(归档日志,用于主从复制和数据归档恢复),InnoDB 有 redo log。为了保证两个日志的一致性,避免主从数据不一致,事务提交采用两阶段提交机制:

  1. 准备阶段(Prepare):InnoDB 执行数据修改,将 redo log 写入磁盘,标记为「Prepare 准备状态」;
  2. 提交阶段(Commit):Server 层将 binlog 写入磁盘并持久化,随后向 InnoDB 发送 commit 指令,InnoDB 将 redo log 标记为「Commit 提交状态」,事务正式提交完成。

两阶段提交的核心价值:保证 redo log 和 binlog 要么都写入成功,要么都失败,不会出现一个日志有记录、另一个没有的情况,从根本上避免了数据不一致、主从同步异常的问题。


三、事务的生命周期与基础语法

3.1 事务的基础语法

复制代码
-- 1. 开启事务(两种写法等价)
BEGIN;
-- 或
START TRANSACTION;

-- 2. 执行事务内的SQL操作(增删改查)
UPDATE account SET balance = balance - 100 WHERE user_id = 1;
UPDATE account SET balance = balance + 100 WHERE user_id = 2;

-- 3. 提交事务:所有操作永久生效
COMMIT;

-- 4. 回滚事务:执行失败时,撤销所有操作,恢复到事务开启前的状态
ROLLBACK;

3.2 自动提交机制

MySQL 默认开启自动提交autocommit = 1),即:

  • 不手动开启事务时,每一条单独的 SQL 语句都是一个独立的事务,执行完成后自动提交;
  • 关闭自动提交(SET autocommit = 0;)后,所有 SQL 都会默认在一个事务中,直到手动执行 COMMITROLLBACK 才会结束事务。

3.3 隐式提交

执行以下语句时,会触发事务的隐式提交 ,相当于提前执行了 COMMIT,破坏事务的原子性,生产环境需避免在事务中执行:

  • DDL 语句:CREATE/ALTER/DROP TABLE 等表结构操作;
  • 管理类语句:FLUSH PRIVILEGESALTER USER 等;
  • 锁语句:LOCK TABLESUNLOCK TABLES 等。

四、事务生产最佳实践

  1. 严格避免长事务 / 大事务长事务会导致 undo log 无法清理,占用大量磁盘空间;锁长时间不释放,引发并发阻塞、死锁;主从同步严重延迟。建议将事务执行时间控制在秒级,禁止分钟级的长事务。

  2. 最小化事务范围只把需要保证原子性的核心操作放在事务中,不要在事务内执行无关操作,比如远程接口调用、循环等待、大量无关查询,避免事务无故拉长。

  3. 合理设置隔离级别生产环境优先使用 InnoDB 默认的 RR 级别,无特殊需求不要修改为更低或更高的级别;对数据实时性要求极高的场景,可改为 RC 级别。

  4. 禁止在事务中混合使用不同存储引擎的表MyISAM 等引擎不支持事务,回滚时无法恢复其数据修改,会导致数据不一致、原子性破坏。

  5. 避免在事务中执行大量批量操作 大批量的 INSERT/UPDATE/DELETE 会拉长事务执行时间,引发锁等待和主从延迟,建议拆分为多个小事务分批执行。

  6. 异常场景必须回滚 业务代码中,事务执行出现异常时,必须手动执行 ROLLBACK 回滚,避免未提交的事务长期持有连接和锁。


五、总结

  1. 事务是数据库保证数据完整性、一致性、可靠性的核心机制,核心是 ACID 四大特性;
  2. 原子性由 undo log 实现,保证操作要么全成、要么全败;
  3. 隔离性由 MVCC + 锁机制实现,保证并发事务互不干扰,平衡安全与性能;
  4. 持久性由 redo log + WAL 机制 + 两阶段提交实现,保证已提交数据永不丢失;
  5. 一致性是事务的最终目标,由数据库底层机制和业务代码共同保障。
相关推荐
weixin_424999362 小时前
html如何修改备注
jvm·数据库·python
21439652 小时前
HTML怎么创建时间轴布局_HTML结构化时间线写法【方法】
jvm·数据库·python
gmaajt2 小时前
HTML函数开发需要SSD吗_SSD对HTML函数开发效率影响【详解】
jvm·数据库·python
LiAo_1996_Y2 小时前
p标签能嵌套div吗_HTML块级元素嵌套规则【解答】
jvm·数据库·python
2301_816660212 小时前
c++怎么将纯C的FILE-升级为C++的fstream_流缓冲绑定技巧【详解】
jvm·数据库·python
码界筑梦坊2 小时前
89-基于Django的加利福尼亚州各县死亡概况分析系统
数据库·python·信息可视化·数据分析·django·毕业设计
m0_514520572 小时前
CSS如何实现输入框提示文字的浮动动画_利用transform translateY上移
jvm·数据库·python
yejqvow122 小时前
php怎么调用字节跳动AI商品推荐_php如何基于用户行为生成千人千面
jvm·数据库·python
weixin_568996062 小时前
HTML怎么离线使用_HTML缓存策略基础配置【教程】
jvm·数据库·python