java - 为什么在可序列化事务中选择后更新会导致死锁?
问题描述
我正在使用注释测试具有不同隔离级别的数据库事务
@Transactional(rollbackFor = Exception.class, isolation = Isolation.SERIALIZABLE)
在春天。
我有一个表“帐户”:
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
在尝试更新时都会抛出,并且记录的余额约为 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 的最终结果,则错误消息显示“尝试重新启动交易”。
推荐阅读
- android - 启动应用程序时出现 React Native 未知错误
- python - 根据列标题获取Excel列字母 - Python
- angular - 如何以角度将富文本显示为工具提示
- bash - ffmpeg:来自图像的视频 - 处理零长度的图像文件
- c++ - 如何在 C++ 中有两个控制台光标?
- javascript - 关于 GrapeJs 框架
- python - 如何编写用于迭代 DataFrame 的 for 循环并将其子集化以仅包含在每次迭代中检索到的那些列?
- json - Json字符串检查VBA
- python - 使用 SimPy 进行业务流程模拟 (BPS)。示例代码
- php - 这是获取 Laravel 中可预订房间列表的功能。谁能帮我弄清楚查询有什么问题?