项目中遇到的问题, spring先新增了一条记录, 然后立即进行查询,但打死查询不到。
这里简单进行总结记录一下。
首先说可能的原因:
- 主从数据同步延迟导致
- 代码事务控制有问题
- 其他应用删除了目标数据
这里直接说结论, 我这边时事务的隔离级别有问题。
好了, 接下来复原一下当时的场景:
代码中会新增数据, 然后再进行查询; 代码示例如下:
@Transactional(propagation = Propagation.REQUIRED)
public PayedResult saveWithdrawOrder(Integer fastOrderNo) {
// 先插入数据, 插入后使用数据库工具进行查询可以查询到数据
methodA();
// 根据ID获取数据, 这里打死获取不到
getById();
}
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public PayedResult methodA(Integer fastOrderNo) {
// 这里是新增数据代码
}
@Transactional(propagation = Propagation.REQUIRES, rollbackFor = Exception.class)
public PayedResult getById(Integer fastOrderNo) {
// 这里根据ID进行查询, 此时使用数据库工具可以查询到新增的数据
}
代码大概就像上面的, 但是死活拿不到数据。 然后新增一个线程可以获取到数据。
因为新增使用的传播机制是 requires_new, 所以肯定是数据已经提交。
此时要注意的是数据隔离级别。 默认是
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT)
此种注解, 隔离级别是使用的数据库默认的。
然后就去查一下数据库隔离级别,
select @@global.tx_isolation;
特么的居然是
此时就什么都明白了, 当前事务使用的是可重复读, 即:一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。当然在可重复读隔离级别下,未提交变更对其他事务也是不可见的。所以是肯定读取不到数据的。
另外也复习一下下隔离级别:
读未提交(READ UNCOMMITTED):一个事务还没提交时,它做的变更就能被别的事务看到。
读提交(READ COMMITTED):一个事务提交之后,它做的变更才会被其他事务看到。
可重复读(REPEATABLE READ):一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。当然在可重复读隔离级别下,未提交变更对其他事务也是不可见的。
串行化(SERIALIZABLE):对于同一行记录,“写”会加“写锁”,“读”会加“读锁”,当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。
修改方案也简单了,将读取方法事务隔离级别修改为 read_commited; 修改如下即可:
@Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRES_NEW)
public PayedResult getById(Integer fastOrderNo) {
// 这里根据ID进行查询, 此时使用数据库工具可以查询到新增的数据
}
因为spring事务传播级别, 如果不新开事务, 那么也会继承父级的事务隔离级别。 所以这里必须使用 required_new;