TCC三阶段
不用纠结复杂语法核心是理解“每个阶段要做什么”以下是简化后的核心代码带通俗注释先定义接口每个服务都要实现3个方法// 账户服务核心接口转账必须走这3步publicinterfaceAccountService{// Try冻结资金打包钱booleantryFreezeMoney(StringaccountId,BigDecimalamount);// Confirm实际扣款/到账送货签收booleanconfirmTransfer(StringaccountId,BigDecimalamount);// Cancel解冻/取消预增退货退款booleancancelTransfer(StringaccountId,BigDecimalamount);}Try阶段冻结资金核心是“锁资源”// A银行账户的Try实现比如A要转1万出去publicbooleantryFreezeMoney(StringaccountId,BigDecimalamount){// 加行锁防止同一时间有人改这个账户余额AccountaccountaccountDao.selectForUpdate(accountId);// 检查余额够不够不够直接返回失败触发Cancelif(account.getBalance().compareTo(amount)0){// 冻结金额把1万从“可用余额”转到“冻结金额”里account.setFrozenAmount(account.getFrozenAmount().add(amount));accountDao.update(account);returntrue;// 冻结成功}returnfalse;// 余额不足冻结失败}Confirm阶段实际完成交易不可逆publicbooleanconfirmTransfer(StringaccountId,BigDecimalamount){AccountaccountaccountDao.select(accountId);// 扣减冻结金额1万从“冻结”变成“实际扣除”account.setBalance(account.getBalance().subtract(amount));account.setFrozenAmount(account.getFrozenAmount().subtract(amount));accountDao.update(account);returntrue;// 扣款成功B银行这边同步执行“确认到账”}Cancel阶段回滚到初始状态补偿操作publicbooleancancelTransfer(StringaccountId,BigDecimalamount){AccountaccountaccountDao.select(accountId);// 解冻冻结金额把1万还回“可用余额”account.setFrozenAmount(account.getFrozenAmount().subtract(amount));accountDao.update(account);returntrue;// 解冻成功B银行这边取消“预增标记”}核心依赖需要一个“事务协调器”比如阿里Seata负责记录每一步的执行状态失败时自动触发Cancel超时后自动重试Confirm。TCC的3个“坑”怎么解决TCC不是完美的实际用的时候会遇到3个核心问题支付宝的解决方案超简单空回滚没冻结资金却要解冻场景A的Try因为网络超时没执行但协调器以为失败触发了Cancel相当于“没打包快递却要退货”解决方案Cancel时先查“有没有冻结记录”没有就直接返回成功不做任何操作ublicbooleancancelTransfer(StringaccountId,BigDecimalamount){AccountaccountaccountDao.select(accountId);// 检查冻结金额够不够不够就是空回滚if(account.getFrozenAmount().compareTo(amount)0){log.warn(没冻结过这笔钱直接返回);returntrue;}// 正常解冻逻辑...}幂等性同一操作被重复执行场景网络重试导致Confirm/Cancel被调用多次比如“快递送了两次签收两次”解决方案给每个交易加唯一IDtxId记录执行状态重复调用直接返回成功// 事务状态表记录每个交易的执行情况classTransactionLog{StringtxId;// 交易唯一IDintstatus;// 0-初始化1-Try成功2-Confirm成功3-Cancel成功}publicbooleanconfirmTransfer(StringtxId,StringaccountId,BigDecimalamount){// 先查这个交易是不是已经Confirm过了if(transactionLogDao.existsByTxIdAndStatus(txId,2)){returntrue;// 已经成功直接返回不重复执行}// 正常Confirm逻辑...}悬挂回滚后之前的Try又执行了场景A的Try超时协调器触发Cancel退货但之后Try又成功执行了相当于“退货后快递又送来了”解决方案Try时先查“交易是不是已经Cancel过了”是就直接返回失败publicbooleantryFreezeMoney(StringtxId,StringaccountId,BigDecimalamount){// 先查这个交易是不是已经回滚过了if(transactionLogDao.existsByTxIdAndStatus(txId,3)){log.error(交易已经回滚不能再冻结);returnfalse;}// 正常Try逻辑...}