简体   繁体   English

找出数据库死锁

[英]Figure out database deadlock

I'm trying to write a springboot code to update the wallet balance based on the DEBIT/CREDIT transactions. 我正在尝试编写springboot代码以根据DEBIT / CREDIT交易更新钱包余额 I've two tables viz. 我有两张桌子,也就是。 wallet and transaction for that. 钱包交易 I'm running a test suite which runs 100 parallel transactions (50 DEBIT and 50 CREDIT). 我正在运行一个测试套件,该套件运行100个并行事务(50借方和50贷方)。 About 50% transactions are failing with the following error, and also the wallet balance in wallet table is not matching the transactions stored in the transaction table 约有50%的交易失败并出现以下错误,并且钱包 表中的钱包余额与交易 表中存储的交易不匹配

com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException:尝试获取锁时发现死锁; try restarting transaction 尝试重新启动事务

I am not able to figure out following 1 )Why Deadlock 2 )Why wallet balance is not matching the number of transactions successfully stored. 我无法找出以下1)为什么死锁2)为什么钱包余额与成功存储的交易数量不匹配。 I'm using MySQL InnoDB for database 我将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());
  }

I fixed the deadlock by using findByIdInWriteMode instead of findById at the line below. 我通过在下面的行中使用findByIdInWriteMode而不是findById修复了死锁。

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

I changed my repo to 我将回购更改为

@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);
}

If you want 100% immediate consistency, you could explore the functional locking concept where you will keep the wallets involved in one transaction in a functional locking table. 如果您希望100%的即时一致性,则可以探索功能锁定概念,将涉及一项交易的钱包保留在功能锁定表中。 So every transaction at the beginning will check if there is any functional lock on the wallet, if so wait till it can get the functional lock otherwise insert into the functional lock and proceed with the transaction. 因此,开始时的每笔交易都会检查钱包中是否有任何功能锁,如果有,请等到它可以得到功能锁为止,否则将其插入功能锁中并继续进行交易。 Adding entries in functional lock table should be atomic with its own commit. 在功能锁表中添加条目应该是原子的,并具有自己的提交。 And at the end of transaction, remove the locking entries. 在交易结束时,删除锁定条目。

If you are looking for eventual consistency(consistency with a delay), you can look to queue the transaction updates and daemons can process the queued items one by one, again using functional locking concept to avoid multiple daemons processing the transactions for the same wallet. 如果您正在寻找最终的一致性(具有延迟的一致性),则可以考虑使事务更新排队,并且守护程序可以一个接一个地处理排队的项目,再次使用功能锁定概念来避免多个守护程序为同一个钱包处理事务。

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

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