什么是事务?
事务(Transaction)是数据库中必须作为单一逻辑工作单元处理的操作集合。事务内的所有操作必须全部成功或全部失败。
典型示例:银行转账
- 从账户A扣款10,000元
- 向账户B入账10,000元
如果步骤1成功但步骤2出错,账户A的钱消失了却没有到达账户B,这是严重问题。事务防止了这种情况。
BEGIN;
UPDATE accounts SET balance = balance - 10000 WHERE id = 'A';
UPDATE accounts SET balance = balance + 10000 WHERE id = 'B';
COMMIT; -- 两个UPDATE都成功后确认
-- 或ROLLBACK; -- 出错时全部回滚
ACID特性
事务必须保证4个核心特性。
A — 原子性(Atomicity)
事务内的操作是**"全有或全无"**的。中途发生错误时,已执行的操作也全部回滚。
C — 一致性(Consistency)
事务执行前后,数据库必须始终保持一致的状态。约束条件(NOT NULL、UNIQUE、FK等)在事务后仍必须得到满足。
I — 隔离性(Isolation)
多个事务同时执行时,每个事务不能看到其他事务的中间状态。必须表现得像顺序执行一样。
D — 持久性(Durability)
事务提交后,即使系统发生故障,数据也必须永久保存。数据库通过WAL(预写式日志)等机制保证这一点。
并发问题
隔离性不完善时可能发生的问题:
脏读(Dirty Read)
读取了其他事务尚未提交的变更。
不可重复读(Non-Repeatable Read)
同一查询两次执行结果不同,因为其他事务在两次查询之间提交了修改。
幻读(Phantom Read)
同一范围查询两次执行的行数不同,因为其他事务插入或删除了行。
隔离级别
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| READ UNCOMMITTED | 发生 | 发生 | 发生 |
| READ COMMITTED | 防止 | 发生 | 发生 |
| REPEATABLE READ | 防止 | 防止 | 发生 |
| SERIALIZABLE | 防止 | 防止 | 防止 |
-- PostgreSQL中设置隔离级别
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN;
-- 事务内操作
COMMIT;
PostgreSQL默认为READ COMMITTED,MySQL InnoDB默认为REPEATABLE READ。
实践建议
- 保持简短:事务应尽量短,以最小化锁争用
- READ COMMITTED:适合大多数OLTP应用
- SERIALIZABLE:用于金融结算等需要严格一致性的场景
- 显式事务:即使使用ORM也要明确设置事务边界
总结
ACID是关系型数据库可靠性的核心原则。在数据库设计阶段明确定义事务边界,并使用ErdmatesERD可视化相关表结构,有助于选择合适的隔离级别和制定有效的锁策略。