事务的基本概念与ACID属性
事务是一组数据库操作的逻辑单元,要么全部成功,要么全部失败。
什么是事务
Java
// 转账场景:A向B转账100元
// 事务包含两个操作,必须同时成功或同时失败
BEGIN TRANSACTION;
UPDATE account SET balance = balance - 100 WHERE id = 'A'; -- A扣100
UPDATE account SET balance = balance + 100 WHERE id = 'B'; -- B加100
COMMIT; -- 或 ROLLBACK;
事务将多个操作绑定为一个原子单元,保证数据一致性。
ACID四大特性
原子性(Atomicity)
事务是不可分割的执行单元,要么全部完成,要么全部不执行。
Java
┌─────────────────────────────────────┐
│ 事务开始 │
├─────────────────────────────────────┤
│ 操作1: A账户扣款 ✓ │
│ 操作2: B账户加款 ✗ (失败) │
├─────────────────────────────────────┤
│ 结果: 全部回滚,A恢复原状 │
└─────────────────────────────────────┘
Java
@Transactional
public void transfer(Long fromId, Long toId, BigDecimal amount) {
accountDao.decrease(fromId, amount);
// 如果此处抛异常,上面的扣款会回滚
accountDao.increase(toId, amount);
}
一致性(Consistency)
事务执行前后,数据库从一个一致状态转换到另一个一致状态。
Java
转账前:
A余额: 500元, B余额: 300元, 总额: 800元
转账后:
A余额: 400元, B余额: 400元, 总额: 800元 ✓ 一致
如果A扣款成功但B加款失败:
A余额: 400元, B余额: 300元, 总额: 700元 ✗ 不一致
事务回滚保证不会出现这种情况
Java
// 约束检查保证一致性
@Entity
public class Account {
@Column(nullable = false)
private BigDecimal balance;
@PreUpdate
public void validate() {
if (balance.compareTo(BigDecimal.ZERO) < 0) {
throw new RuntimeException("余额不能为负");
}
}
}
隔离性(Isolation)
并发事务之间相互隔离,一个事务的中间状态对其他事务不可见。
Java
时间线:
┌─────────────────────────────────────────────────┐
│ 事务A: 开始 ──────────────────────────── 提交 │
│ │ │ │
│ 事务B: │─ 开始 ───────────── 提交 ────│─→ │
│ │ (读不到A的中间状态) │ │
└─────────────────────────────────────────────────┘
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| READ UNCOMMITTED | ✓ | ✓ | ✓ |
| READ COMMITTED | ✗ | ✓ | ✓ |
| REPEATABLE READ | ✗ | ✗ | ✓ |
| SERIALIZABLE | ✗ | ✗ | ✗ |
text
@Transactional(isolation = Isolation.READ_COMMITTED)
public Account getAccount(Long id) {
return accountDao.findById(id);
}
持久性(Durability)
事务一旦提交,对数据的修改是永久性的,即使系统崩溃也不会丢失。
text
事务提交流程:
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ 内存数据 │ → │ 写入日志 │ → │ 刷入磁盘 │
│ 修改完成 │ │ (redo log) │ │ (持久化) │
└──────────────┘ └──────────────┘ └──────────────┘
↓
即使崩溃也能恢复
通过redo log和undo log保证持久性,数据库崩溃后可恢复已提交的事务。
ACID的关系
text
┌─────────────────────┐
│ 一致性 │
│ (最终目标) │
└─────────────────────┘
↑
┌────────────┼────────────┐
│ │ │
┌───┴───┐ ┌───┴───┐ ┌───┴───┐
│原子性 │ │隔离性 │ │持久性 │
│(保证) │ │(保证) │ │(保证) │
└───────┘ └───────┘ └───────┘
- 原子性保证操作不可分割
- 隔离性保证并发不干扰
- 持久性保证提交不丢失
- 三者共同保证一致性
Spring事务与ACID
text
@Service
public class OrderService {
// Spring @Transactional 保证ACID
@Transactional
public void createOrder(Order order) {
// 原子性: 整个方法要么成功要么回滚
orderDao.insert(order);
// 一致性: 库存不能为负
inventoryService.decrease(order.getProductId(), order.getQuantity());
// 隔离性: 配置隔离级别控制
// 持久性: 提交后数据持久化
}
}
事务边界问题
text
// 错误:事务边界不清晰
public void process() {
step1(); // 事务1
step2(); // 事务2
// step1成功step2失败,无法保证整体原子性
}
// 正确:统一事务边界
@Transactional
public void process() {
step1();
step2();
// 要么都成功,要么都回滚
}
要点总结
- 原子性:操作不可分割,要么全成功要么全回滚
- 一致性:数据从一个有效状态转换到另一个有效状态
- 隔离性:并发事务之间相互隔离
- 持久性:提交后数据永久保存
- @Transactional注解实现声明式事务管理
- 合理设置事务边界是保证ACID的关键
📝 发现内容有误?点击此处直接编辑