要保证多个系统间的数据一致性,常用的方案是两阶段提交(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调用,可以实现二阶段提交,确保转账要么全部成功要么全部失败。