最近开发碰到一个问题,事务B中的查询需要用到事务A中提交的数据,所以我就让事务B循环查询数据库,等待事务A提交后再继续执行。但是最后发现事务B怎么等也等不到事务A的数据=.= (这里有个前提,事务AB所在方法都是主方法执行完毕后开启的异步方法,事务A和事务B无法控制开启的先后顺序),所以浅看了一下事务是怎么个回事。
这里首先要清楚事务的四种隔离级别:
事务隔离级别:
- Read Uncommitted(未提交读);
- Read Committed(已提交读):解决脏读;
- Repeatable Read(可重复读):解决脏读、不可重复读;
- Serializable(串行化):解决事务并发的所有问题。
脏读:读到其他事务未提交的数据(脏数据);
不可重复读:第一次读取独到其他事务提交前的数据,第二次读取读到该事务提交后的数据;
幻读:第一次读取未读到其他事务提交后新增的行数据,第二次读取读到其他事务提交后新增的行数据。
可重复读是InnoDB默认的隔离级别,看到这里问题原因大概就明晰了,可重复读这一隔离级别要求同一事务多次查询的结果是一致的,而我的事务B第一次查询执行的时候事务A还没有提交,那么事务B之后每次的查询都只能查到和第一次查询相同的数据,即使事务A提交的也查不到数据库中新增的数据。
所以要解决这个问题有两个方案:
- 把数据库的隔离级别降到已提交读:
相关命令:
1.查看当前会话隔离级别:
select @@tx_isolation;
2.查看系统当前隔离级别:
select @@global.tx_isolation;
3.设置当前会话隔离级别:
set session transaction isolatin level repeatable read;
4.设置系统当前隔离级别:
set global transaction isolation level repeatable read;
- 确保事务B执行开始于事务A提交后:
相关代码:
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
@SneakyThrows
@Override
public void afterCompletion(int status) {
if (status == STATUS_COMMITTED) {
log.info("事务已提交,执行后续任务);
}
}
});
Mysql InnoDB对事务隔离级别的支持程度(并发性能由高到低):
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
未提交读 | 可能 | 可能 | 可能 |
已提交读 | 不可能 | 可能 | 可能 |
可重复读(默认) | 不可能 | 不可能 | 对InnoDB不可能 |
串行化 | 不可能 | 不可能 | 不可能 |
(在可重复读这一隔离级别,InnoDB通过间隙锁策略防止了幻读的出现)