在之前的讨论中,我们已经了解了脏读、不可重复读和幻读这些并发问题,它们都源于多个事务同时执行时可能产生的数据不一致。要深入理解这些问题以及如何解决,就需要先掌握数据库事务的核心概念。
下面我会从事务的定义、ACID 属性、事务控制语句、隔离级别、实现机制以及实际应用这几个方面,为你系统地介绍数据库事务。
1. 什么是事务?
事务(Transaction)是数据库管理系统(DBMS)中执行的一组操作,这些操作要么全部成功,要么全部失败,不会出现部分成功、部分失败的情况。它是一个不可分割的工作单元。
典型场景:银行转账
- 从账户A扣除100元
- 向账户B增加100元
这两个操作必须作为一个整体来执行。如果只执行了第一步而第二步失败,数据库就会处于不一致状态。事务保证要么两步都完成,要么都不发生。
2. ACID 属性
事务的四大特性通常用 ACID 来概括:
A:原子性(Atomicity)
事务中的所有操作被视为一个原子单元,不可分割。
- 如果事务执行过程中发生错误,已经执行的操作会回滚(Rollback),使数据库回到事务开始前的状态。
- 实现机制:回滚日志(Undo Log) 记录操作前的旧值,以便在回滚时恢复。
C:一致性(Consistency)
事务必须使数据库从一个一致性状态转换到另一个一致性状态。
- 一致性由应用层定义:例如转账前后,账户总金额不变;账户余额不能为负等。
- 数据库通过约束(主键、外键、唯一性、检查约束)以及原子性、隔离性来帮助保证一致性。
I:隔离性(Isolation)
多个事务并发执行时,一个事务的执行不应被其他事务干扰。
- 隔离性通过锁机制和多版本并发控制(MVCC) 来实现。
- SQL 标准定义了四种隔离级别,级别越高,隔离性越强,但并发性能越低。
D:持久性(Durability)
一旦事务提交,其对数据库的修改就是永久性的,即使系统发生故障(如断电、崩溃),数据也不会丢失。
- 实现机制:重做日志(Redo Log) 在事务提交前将修改写入磁盘,保证崩溃后可以恢复。
3. 事务控制语句
在 SQL 中,事务通常通过以下语句控制:
BEGIN或START TRANSACTION:显式开始一个事务。COMMIT:提交事务,使所有修改永久生效。ROLLBACK:回滚事务,撤销当前事务中已执行的操作。SAVEPOINT:设置保存点,允许部分回滚。RELEASE SAVEPOINT:删除保存点。ROLLBACK TO SAVEPOINT:回滚到指定保存点。
示例(MySQL):
START TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
-- 检查无误后提交
COMMIT;
-- 如果出错则执行 ROLLBACK;
4. 并发问题与隔离级别
前面文章已经介绍了三种常见的并发问题:
| 问题 | 描述 |
|---|---|
| 脏读 | 读到其他事务未提交的数据 |
| 不可重复读 | 同一事务内两次读取同一行数据,结果不同 |
| 幻读 | 同一事务内两次范围查询,结果集行数不同 |
SQL 标准定义了四种隔离级别,每种级别能避免的问题如下:
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| 读未提交 | 可能 | 可能 | 可能 |
| 读已提交 | 不可能 | 可能 | 可能 |
| 可重复读 | 不可能 | 不可能 | 可能(理论上) |
| 串行化 | 不可能 | 不可能 | 不可能 |
- 读未提交:性能最高,但几乎没有隔离性。
- 读已提交:大多数数据库(如 Oracle、SQL Server)的默认级别,避免脏读。
- 可重复读:MySQL InnoDB 的默认级别,通过 MVCC + 间隙锁基本解决了幻读。
- 串行化:最高隔离级别,所有事务串行执行,性能最低。
5. 事务的实现机制
数据库如何实现 ACID?主要依赖以下技术:
5.1 日志(Logging)
- Undo Log:记录事务修改前的数据,用于回滚。
- Redo Log:记录事务修改后的数据,用于故障恢复。
- Binlog(MySQL):用于主从复制和基于时间点的恢复。
5.2 锁机制(Locking)
数据库使用锁来控制并发访问:
- 共享锁(S锁):允许事务读取数据,其他事务只能加共享锁,不能加排他锁。
- 排他锁(X锁):允许事务修改数据,阻止其他事务加任何锁。
- 意向锁:用于层次锁(表级、行级)的协调。
- 间隙锁(Gap Lock):锁定索引记录之间的间隙,防止幻读(MySQL InnoDB)。
5.3 多版本并发控制(MVCC)
MVCC 是“可重复读”和“读已提交”级别常用的并发控制技术。
- 每行数据保存多个版本,通过创建版本号和删除版本号(或事务ID)来管理可见性。
- 读操作通常读取“快照”,不加锁(快照读),写操作加锁(当前读)。
- 这样读写不互斥,极大提高了并发性能。
6. 事务在实践中的注意事项
6.1 事务的粒度与时长
事务应尽可能短小,避免长时间占用资源。
- 不要在事务中执行耗时的非数据库操作(如网络调用、复杂计算)。
- 长事务可能导致锁等待、死锁、Undo 膨胀等问题。
6.2 死锁处理
多个事务互相等待对方释放锁时,就会发生死锁。
- 数据库通常能自动检测死锁,并回滚其中一个事务。
- 开发时可以通过约定加锁顺序、使用较低隔离级别、快速提交等方式减少死锁。
6.3 隔离级别的选择
- 对一致性要求不高、追求高并发时,可用“读已提交”。
- 需要防止不可重复读和幻读时,选择“可重复读”或“串行化”。
- 注意不同数据库的默认级别和实现差异(如 MySQL 可重复读与 Oracle 可重复读表现不同)。
6.4 自动提交模式
很多数据库客户端默认开启自动提交,每个 SQL 语句就是一个独立的事务。
- 在需要多个语句组成一个事务时,务必显式开启事务(
START TRANSACTION)并关闭自动提交。
7. 总结
数据库事务是保证数据一致性和可靠性的核心机制。
- ACID 定义了事务必须具备的特性。
- 隔离级别 在一致性与并发性能之间提供权衡。
- 日志、锁、MVCC 是数据库实现事务的三大支柱。
- 在实际开发中,合理设计事务边界、选择合适的隔离级别、监控死锁和长事务,是写出健壮应用的关键。
如果你对某个具体方面(比如 MVCC 的详细原理、死锁排查方法、特定数据库的实现差异)感兴趣,我可以继续深入讲解。

480

被折叠的 条评论
为什么被折叠?



