事务管理完全指南
字数
1552 字
阅读时间
7 分钟
1 事务的定义
事务(Transaction)是一个操作序列,这些操作要么都执行,要么都不执行,是一个不可分割的工作单位。事务是数据库维护数据一致性的单位,在数据库提交工作时,可确保要么所有修改都已保存,要么所有修改都不保存。
2 事务的特性(ACID)
事务具有四个特性,通常称为ACID特性:
2.1 原子性(Atomicity)
- 事务是不可分割的最小操作单位
- 事务中的所有操作要么全部完成,要么全部失败回滚
- 不存在部分完成的情况
2.2 一致性(Consistency)
- 事务必须使数据库从一个一致性状态变换到另一个一致性状态
- 保证数据库的完整性约束
- 例如:转账前后,两个账户的总金额保持不变
2.3 隔离性(Isolation)
- 多个事务并发执行时,一个事务的执行不应影响其他事务的执行
- 通过事务隔离级别实现
- 不同隔离级别提供不同程度的隔离性
2.4 持久性(Durability)
- 一个事务一旦提交,其对数据库的修改将永久保存
- 即使系统崩溃,提交的数据也不会丢失
3 并发问题
在多个事务并发执行时,可能会出现以下问题:
3.1 脏读(Dirty Read)
- 一个事务读取了另一个未提交事务修改过的数据
- 如果另一个事务回滚,那么当前事务读取的数据就是无效的
3.2 不可重复读(Non-repeatable Read)
- 一个事务内多次读取同一数据,但其他事务在这期间修改了数据
- 导致两次读取的结果不一致
3.3 幻读(Phantom Read)
- 一个事务内多次查询某个范围的记录,但其他事务在这期间插入了新的记录
- 导致两次查询返回的记录数不一致
4 MySQL事务隔离级别
4.1 READ UNCOMMITTED(读未提交)
- 最低的隔离级别
- 允许读取未提交的数据
- 可能出现脏读、不可重复读和幻读
- 实际应用中很少使用
4.2 READ COMMITTED(读已提交)
- 只能读取已经提交的数据
- 可以避免脏读
- 可能出现不可重复读和幻读
- Oracle数据库默认级别
4.3 REPEATABLE READ(可重复读)
- 保证同一事务中多次读取同样数据的结果一致
- 可以避免脏读和不可重复读
- 可能出现幻读
- MySQL默认的隔离级别
4.4 SERIALIZABLE(串行化)
- 最高的隔离级别
- 完全串行化的读写,事务之间完全隔离
- 可以避免脏读、不可重复读和幻读
- 性能最差,很少使用
4.5 隔离级别对比
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|-------- -|------|------------|-- ----|
| READ UNCOMMITTED | 可能 | 可能 | 可能 |
| READ COMMITTED | 不可能 | 可能 | 可能 |
| REPEATABLE READ | 不可能 | 不可能 | 可能 |
| SERIALIZABLE | 不可能 | 不可能 | 不可能 |
4.6 MySQL设置隔离级别
sql
-- 查看当前隔离级别
SELECT @@transaction_isolation;
-- 设置当前会话隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- 设置全局隔离级别
SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;
5 需要使用事务管理的场景
涉及多表操作
- 多个表数据需要同时更新
- 保证数据的一致性
金融交易
- 转账
- 支付
- 退款
库存管理
- 下单减库存
- 订单取消恢复库存
数据批量处理
- 批量导入数据
- 批量更新操作
关键业务操作
- 用户注册
- 订单处理
- 积分变更
6 Java事务管理实现
6.1 JDBC事务
java
Connection conn = null;
try {
// 获取连接
conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
// 关闭自动提交
conn.setAutoCommit(false);
// 执行SQL操作
Statement stmt = conn.createStatement();
stmt.execute("UPDATE account SET balance = balance - 100 WHERE id = 1");
stmt.execute("UPDATE account SET balance = balance + 100 WHERE id = 2");
// 提交事务
conn.commit();
} catch (Exception e) {
// 发生异常,回滚事务
if (conn != null) {
conn.rollback();
}
throw e;
} finally {
// 恢复自动提交
if (conn != null) {
conn.setAutoCommit(true);
}
}
6.2 JTA事务
java
@Resource
UserTransaction utx;
try {
utx.begin();
// 执行业务逻辑
userDAO.updateUser(user);
orderDAO.createOrder(order);
utx.commit();
} catch (Exception e) {
utx.rollback();
throw e;
}
7 Spring事务管理
7.1 声明式事务
java
@Transactional
public class UserService {
@Transactional(rollbackFor = Exception.class)
public void transfer(Long fromId, Long toId, BigDecimal amount) {
Account fromAccount = accountDao.getAccount(fromId);
Account toAccount = accountDao.getAccount(toId);
fromAccount.setBalance(fromAccount.getBalance().subtract(amount));
toAccount.setBalance(toAccount.getBalance().add(amount));
accountDao.updateAccount(fromAccount);
accountDao.updateAccount(toAccount);
}
}
7.2 编程式事务
java
@Autowired
private TransactionTemplate transactionTemplate;
public void transfer(final Long fromId, final Long toId, final BigDecimal amount) {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(TransactionStatus status) {
try {
Account fromAccount = accountDao.getAccount(fromId);
Account toAccount = accountDao.getAccount(toId);
fromAccount.setBalance(fromAccount.getBalance().subtract(amount));
toAccount.setBalance(toAccount.getBalance().add(amount));
accountDao.updateAccount(fromAccount);
accountDao.updateAccount(toAccount);
} catch (Exception e) {
status.setRollbackOnly();
throw e;
}
}
});
}
7.3 Spring事务传播行为
- REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务
- SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行
- MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常
- REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起
- NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起
- NEVER:以非事务方式运行,如果当前存在事务,则抛出异常
- NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行
7.4 Spring事务配置示例
java
@Configuration
@EnableTransactionManagement
public class TransactionConfig {
@Bean
public DataSource dataSource() {
return new DruidDataSource();
}
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean
public TransactionTemplate transactionTemplate(PlatformTransactionManager transactionManager) {
return new TransactionTemplate(transactionManager);
}
}
`
``