天天看点

如何保证多个系统间的数据一致性?

要保证多个系统间的数据一致性,常用的方案是两阶段提交(2PC,Two-Phase Commit)。它的基本原理是:

第一阶段(Prepare phase):事务的发起方请求所有参与方准备提交事务。参与方接收到请求后,会将事务的操作记录到本地日志中作为准备提交状态。然后参与方决定是否能准备提交该事务,并将决定通知给发起方。

第二阶段(Commit phase):发起方根据所有参与方的反馈,决定是提交(commit)事务,还是取消(abort)事务。

- 如果所有参与方均表明可以准备提交(voted commit),发起方会发出提交请求。参与方接收到提交请求后,会正式将事务的操作提交,并承诺无论发生任何故障,这些操作都不会被撤销。

- 如果任一参与方表明无法准备提交(voted abort),发起方会发出取消请求。参与方接收到取消请求后,会撤销之前准备提交的操作,并将事务恢复到开始时的状态。

整个两阶段提交的过程如下图所示:

如何保证多个系统间的数据一致性?

两阶段提交能确保所有参与方对事务的决定达成一致,要么都提交,要么都取消。这样可以保证不同系统之间的数据强一致性。它的缺点是如果在第二阶段提交时发生故障,很难确定事务的最终状态,可能导致数据不一致。

这里是基于REST API的实现示例:

java
// 1. /prepare API - 更新数据库但不提交
@RequestMapping("/prepare")
public ResponseEntity<String> prepare(HttpServletRequest request) {
    Transfer transfer = getTransferFromRequest(request); 
    transferService.prepareTransfer(transfer);
    return ResponseEntity.ok("Transaction prepared"); 
}

// 2. /commit API - 提交事务
@RequestMapping("/commit")
public ResponseEntity<String> commit(HttpServletRequest request) {
    Transfer transfer = getTransferFromRequest(request); 
    transferService.commitTransfer(transfer);
    return ResponseEntity.ok("Transaction committed");
}

// 3. /rollback API - 回滚事务 
@RequestMapping("/rollback")
public ResponseEntity<String> rollback(HttpServletRequest request) {
    Transfer transfer = getTransferFromRequest(request);
    transferService.rollbackTransfer(transfer);
    return ResponseEntity.ok("Transaction rolled back"); 
}
           

TransferService类的实现:

java
@Service
public class TransferService {
    @Autowired
    private TransferDao transferDao;

    public void prepareTransfer(Transfer transfer) {
        Connection conn = transferDao.getConnection();
        conn.setAutoCommit(false);   // 关闭自动提交
        
        transferDao.debitAccount(conn, transfer.getFromAccountId(), transfer.getAmount());
        transferDao.creditAccount(conn, transfer.getToAccountId(), transfer.getAmount());
    }
    
    public void commitTransfer(Transfer transfer) {
        Connection conn = transferDao.getConnection();
        conn.commit();  // 提交事务
    }
    
    public void rollbackTransfer(Transfer transfer) {
        Connection conn = transferDao.getConnection();
        conn.rollback(); // 回滚事务
    }
} 
           

TransferDao类用于执行数据库操作:

java
@Repository
public class TransferDao {
    public Connection getConnection() throws SQLException {
        // 获取数据库连接...
    }
    
    public void debitAccount(Connection conn, int accountId, int amount) throws SQLException { 
        // 更新账号余额,减去amount...
    }
    
    public void creditAccount(Connection conn, int accountId, int amount) throws SQLException  {
       // 更新账号余额,加上amount...
    } 
}
           

在这个例子中:

1. /prepare API会关闭自动提交,执行转账操作但不提交。

2. /commit API会提交事务,完成转账。

3. /rollback API会回滚事务,撤销转账。

通过这三个API调用,可以实现二阶段提交,确保转账要么全部成功要么全部失败。

继续阅读