繁体   English   中英

为什么在可序列化事务中选择后更新会导致死锁?

[英]Why update after select in serializable transaction cause dead lock?

我正在使用注释测试具有不同隔离级别的数据库事务

@Transactional(rollbackFor = Exception.class, isolation = Isolation.SERIALIZABLE)在 Spring 中。

我有一个表“帐户”:

create table account (id int primary key, balance int);

和里面的一个记录:

insert into account values(1, 999);

我写了一些Java代码,在一个事务中基本上类似于以下SQL:

select * from account where id=1;
update account set balance=balance-1 where id=1;

我创建了 999 个线程,每个线程运行一次。

我期望记录的余额为 0,但大多数线程抛出org.springframework.dao.DeadlockLoserDataAccessException: Deadlock found when trying to get lock; try restarting transaction org.springframework.dao.DeadlockLoserDataAccessException: Deadlock found when trying to get lock; try restarting transaction更新时org.springframework.dao.DeadlockLoserDataAccessException: Deadlock found when trying to get lock; try restarting transaction ,记录剩余约 700 余额。

我认为在可序列化事务中选择时,事务在它读取的行上获得读锁,并且只有在提交后才会释放它,这样其他事务在前者被提交之前无法选择这些行。

我对可序列化、事务或锁有什么误解吗? 或者是我在 Java 中的实现导致了问题? 我正在将 myBatis 与 mySQL 一起使用。 这是我用于交易的实际 Java 代码:

@Service
@RequiredArgsConstructor(onConstructor_ = {@Autowired})
@Transactional(rollbackFor = Exception.class, isolation = Isolation.SERIALIZABLE)
public class MyTestService {

    private final AccountMapper accountMapper;

    public void decreaseMoneyByOne(String id){
        Account account = accountMapper.selectByPrimaryKey(id);
        int balanceBefore = account.getBalance();
        account.setBalance(balanceBefore - 1);
        accountMapper.updateByPrimaryKey(account);
    }
}

以下是 Postgres 如何进行序列化(Serializable Snapshot Isolation)。 如果您使用不同的数据库,它可能使用不同的机制。 真正的数据库不会按照您描述的方式进行操作,因为这基本上是一种幼稚的实现,锁定所有这些行的性能会很差。 有更轻(虽然更复杂)的方法,包括终止死锁事务等。

如果您想获得余额为 0 的最终结果,则错误消息显示“尝试重新启动交易”。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM