找出数据库死锁



我正在尝试编写一个springboot代码,以根据借记/贷记交易更新钱包余额。 我有两个表,即钱包交易。 我正在运行一个测试套件,它运行 100 个并行事务(50 个借方和 50 个贷方(。大约 50% 的交易失败并出现以下错误,并且钱包表中的钱包余额与存储在交易表中的交易不匹配

com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException:尝试获取锁定时发现死锁;尝试重新启动事务

我无法弄清楚以下 1 (为什么死锁 2 (为什么钱包余额与成功存储的交易数量不匹配。我是 使用MySQL InnoDB用于数据库

@Transactional(isolation = Isolation.SERIALIZABLE)
public Transaction saveTransaction(String walletId, Txn txn) {
Optional<Wallet> byId = walletRepo.findById(Integer.parseInt(walletId));
if (!byId.isPresent()) {
throw new WalletNotFoundException();
}
Wallet wallet = byId.get();
Transaction transaction = new Transaction();
transaction.setAmount(txn.getAmount());
transaction.setType(txn.getType());
transaction.setWallet(wallet);
BigDecimal balance = applyTransactionToWallet(txn, wallet.getBalance());
Transaction save = transactionRepo.save(transaction);
wallet.setBalance(balance);
return save;
}
public Optional<Wallet> getWallet(String walletId) {
Optional<Wallet> byId = walletRepo.findById(Integer.parseInt(walletId));
return byId;
}
private BigDecimal applyTransactionToWallet(Txn txn, BigDecimal amount) {
if (txn.getType() == TransactionType.CREDIT) {
return amount.add(txn.getAmount());
}
return amount.subtract(txn.getAmount());
}

我通过使用findByIdInWriteMode而不是在下面一行findById来修复死锁。

Optional<Wallet> byId = walletRepo.findById(Integer.parseInt(walletId));

我将我的存储库更改为

@Repository
public interface WalletRepo extends JpaRepository<Wallet, Integer> {
@Query(value = "FROM Wallet W where W.walletId = :id")
@Lock(LockModeType.PESSIMISTIC_WRITE)
Optional<Wallet> findByIdInWriteMode(@Param("id") Integer id);
}

如果你想要100%的即时一致性,你可以探索功能锁定的概念,你将把钱包保存在一个功能锁定表中的一笔交易中。因此,开始时的每笔交易都会检查钱包上是否有任何功能锁,如果是这样,请等到它可以获得功能锁,否则插入功能锁并继续交易。 在功能锁表中添加条目应该是原子的,并具有自己的提交。在交易结束时,删除锁定条目。

如果您正在寻找最终一致性(延迟一致性(,您可以查看对交易更新进行排队,守护进程可以逐个处理排队的项目,再次使用功能锁定概念来避免多个守护进程处理同一钱包的交易。

最新更新