Java事务管理与隔离级别
事务是数据库操作的基本单位,保证操作的原子性和一致性。
事务ACID特性
| 特性 | 说明 |
|---|---|
| Atomicity(原子性) | 事务要么全部成功,要么全部失败 |
| Consistency(一致性) | 事务前后数据状态一致 |
| Isolation(隔离性) | 事务之间互不干扰 |
| Durability(持久性) | 事务提交后永久保存 |
JDBC事务管理
手动事务管理
Java
Connection conn = dataSource.getConnection();
try {
conn.setAutoCommit(false); // 关闭自动提交
// 执行多个SQL
Statement stmt = conn.createStatement();
stmt.executeUpdate("UPDATE accounts SET balance = balance - 100 WHERE id = 1");
stmt.executeUpdate("UPDATE accounts SET balance = balance + 100 WHERE id = 2");
conn.commit(); // 提交事务
} catch (SQLException e) {
conn.rollback(); // 回滚事务
throw e;
} finally {
conn.setAutoCommit(true); // 恢复自动提交
conn.close();
}
设置保存点
Java
Connection conn = dataSource.getConnection();
try {
conn.setAutoCommit(false);
Statement stmt = conn.createStatement();
stmt.executeUpdate("INSERT INTO orders VALUES(1, 'order1')");
Savepoint sp1 = conn.setSavepoint("sp1"); // 设置保存点
stmt.executeUpdate("INSERT INTO orders VALUES(2, 'order2')");
// 可回滚到保存点
conn.rollback(sp1); // 只回滚到sp1,保留第一条
conn.commit();
} catch (SQLException e) {
conn.rollback();
} finally {
conn.close();
}
事务隔离级别
四种隔离级别
| 隔离级别 | 脏读 | 不可重复读 | 幻读 | 性能 |
|---|---|---|---|---|
| READ_UNCOMMITTED | 可能 | 可能 | 可能 | 最高 |
| READ_COMMITTED | 不可能 | 可能 | 可能 | 高 |
| REPEATABLE_READ | 不可能 | 不可能 | 可能 | 中 |
| SERIALIZABLE | 不可能 | 不可能 | 不可能 | 最低 |
并发问题详解
Java
脏读:读到其他事务未提交的数据
┌─────────┬──────────────────┐
│ 事务A │ UPDATE balance=0 │
│ │ (未提交) │
├─────────┼──────────────────┤
│ 事务B │ SELECT balance │
│ │ 读到0(脏读) │
├─────────┼──────────────────┤
│ 事务A │ ROLLBACK │
│ │ balance恢复100 │
└─────────┴──────────────────┘
不可重复读:同一事务两次读取结果不同
┌─────────┬──────────────────┐
│ 事务A │ SELECT balance=100│
├─────────┼──────────────────┤
│ 事务B │ UPDATE balance=50 │
│ │ COMMIT │
├─────────┼──────────────────┤
│ 事务A │ SELECT balance=50 │
│ │ 前后不一致 │
└─────────┴──────────────────┘
幻读:同一事务两次查询记录数不同
┌─────────┬──────────────────┐
│ 事务A │ SELECT COUNT=10 │
├─────────┼──────────────────┤
│ 事务B │ INSERT 新记录 │
│ │ COMMIT │
├─────────┼──────────────────┤
│ 事务A │ SELECT COUNT=11 │
│ │ 多了一条(幻读) │
└─────────┴──────────────────┘
JDBC设置隔离级别
SQL
Connection conn = dataSource.getConnection();
// 设置隔离级别
conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
// 隔离级别常量
Connection.TRANSACTION_READ_UNCOMMITTED // 读未提交
Connection.TRANSACTION_READ_COMMITTED // 读已提交(MySQL默认)
Connection.TRANSACTION_REPEATABLE_READ // 可重复读(MySQL InnoDB默认)
Connection.TRANSACTION_SERIALIZABLE // 序列化
// 查看当前隔离级别
int level = conn.getTransactionIsolation();
MySQL隔离级别
Java
-- 查看隔离级别
SELECT @@transaction_isolation;
-- 设置隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- InnoDB默认:REPEATABLE_READ
-- 且通过MVCC解决幻读,性能较好
Spring事务管理
@Transactional注解
Java
@Service
public class UserService {
@Transactional
public void transfer(int fromId, int toId, int amount) {
accountDao.decreaseBalance(fromId, amount);
accountDao.increaseBalance(toId, amount);
}
@Transactional(
propagation = Propagation.REQUIRED, // 传播行为
isolation = Isolation.READ_COMMITTED, // 隤离级别
timeout = 30, // 超时秒数
readOnly = false, // 只读
rollbackFor = Exception.class // 回滚异常
)
public void complexOperation() {
// ...
}
}
传播行为
| 传播行为 | 说明 |
|---|---|
| REQUIRED | 有事务则加入,无则新建(默认) |
| REQUIRES_NEW | 总是新建事务,挂起已有事务 |
| SUPPORTS | 有事务则加入,无则非事务执行 |
| NOT_SUPPORTED | 非事务执行,挂起已有事务 |
| MANDATORY | 必须在事务中,否则抛异常 |
| NEVER | 必须非事务执行,否则抛异常 |
| NESTED | 有事务则嵌套执行 |
Java
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void independentTransaction() {
// 独立事务,不受外部事务影响
}
@Transactional(propagation = Propagation.NESTED)
public void nestedTransaction() {
// 嵌套事务,可独立回滚不影响外层
}
事务回滚规则
Java
@Transactional(rollbackFor = Exception.class) // 所有异常回滚
@Transactional(rollbackFor = {SQLException.class, IOException.class})
@Transactional(noRollbackFor = BusinessException.class) // 不回滚
// 默认:只对RuntimeException和Error回滚
// checked异常不回滚,需显式指定
编程式事务
Java
@Service
public class UserService {
@Autowired
private TransactionTemplate transactionTemplate;
public void transfer(int fromId, int toId, int amount) {
transactionTemplate.execute(status -> {
accountDao.decreaseBalance(fromId, amount);
accountDao.increaseBalance(toId, amount);
return null;
});
}
}
分布式事务
问题场景
Java
服务A ───→ DB1 ───→ 扣款成功
服务B ───→ DB2 ───→ 加款失败
如何保证跨库事务一致性?
解决方案
| 方案 | 特点 |
|---|---|
| 2PC/XA | 强一致性,性能差 |
| TCC | 最终一致,需补偿代码 |
| Seata | 阿里开源,支持多种模式 |
| Saga | 长事务,编排补偿 |
| 本地消息表 | 最终一致,简单可靠 |
Java
// Seata @GlobalTransactional
@GlobalTransactional
public void distributedTransfer(int fromId, int toId, int amount) {
accountServiceA.decrease(fromId, amount);
accountServiceB.increase(toId, amount);
}
事务最佳实践
1. 事务范围最小化
Java
// 不推荐:事务范围大
@Transactional
public void processOrder(Order order) {
validateOrder(order); // 校验(不需要事务)
calculatePrice(order); // 计算(不需要事务)
saveOrder(order); // 保存(需要事务)
sendNotification(order); // 发通知(不需要事务)
}
// 推荐:事务范围小
public void processOrder(Order order) {
validateOrder(order);
calculatePrice(order);
saveOrderInTransaction(order); // 只在这里开启事务
sendNotification(order);
}
@Transactional
private void saveOrderInTransaction(Order order) {
orderDao.save(order);
}
2. 避免长事务
text
// 不推荐:长事务
@Transactional
public void batchProcess(List<Item> items) {
for (Item item : items) {
processItem(item); // 1000个处理,事务很长
}
}
// 推荐:分批短事务
public void batchProcess(List<Item> items) {
for (Item item : items) {
processItemInTransaction(item); // 每个独立事务
}
}
3. 只读查询不加事务
text
// 不推荐:查询也加事务
@Transactional
public List<User> findAll() {
return userDao.findAll();
}
// 推荐:只读不加事务,或只读事务
@Transactional(readOnly = true)
public List<User> findAll() {
return userDao.findAll();
}
注意事项
事务方法必须是public
同类调用事务方法失效(绕过代理)
长事务占用连接,影响性能
只读事务可优化数据库行为
分布式事务需专门方案解决
要点总结
- 事务ACID:原子性、一致性、隔离性、持久性
- 四种隔离级别:读未提交、读已提交、可重复读、序列化
- Spring @Transactional管理事务,设置传播行为和隔离级别
- 事务范围应最小化,避免长事务
- 分布式事务使用Seata/TCC/Saga等方案
📝 发现内容有误?点击此处直接编辑