锦州市网站建设_网站建设公司_图标设计_seo优化
2026/1/10 1:40:19 网站建设 项目流程

一、问题定义与核心概念

1.1 问题定义

悬挂问题(Hanging)

text

复制

下载

正常流程:Try → Confirm/Cancel 悬挂场景:Try执行成功 → 网络超时 → 事务管理器调用Cancel → Cancel未执行 结果:预留的资源无法释放,形成悬挂事务

空回滚问题(Empty Rollback)

text

复制

下载

正常流程:Try失败 → 调用Cancel 空回滚场景:Try未执行(网络超时) → 事务管理器超时触发Cancel → Cancel无Try记录可回滚 结果:执行了无效的Cancel操作

二、悬挂问题解决方案

2.1 状态标记法

java

复制

下载

@Component public class HangPreventionService { @Autowired private TransactionStateStore stateStore; /** * Try阶段:记录事务状态 */ @Transactional public void tryPhase(String xid, String businessId) { // 1. 检查是否存在未完成的同一业务事务 TransactionState existing = stateStore.getUnfinishedByBusinessId(businessId); if (existing != null && !isExpired(existing)) { throw new TransactionHangingException("存在未完成事务,请稍后重试"); } // 2. 插入事务记录 TransactionState state = new TransactionState(); state.setXid(xid); state.setBusinessId(businessId); state.setStatus(TransactionStatus.TRYING); state.setCreateTime(new Date()); state.setExpireTime(DateUtils.addMinutes(new Date(), 30)); stateStore.save(state); // 3. 执行业务Try逻辑 businessService.tryOperation(businessId); } /** * Cancel阶段:检查并处理悬挂 */ @Transactional public void cancelPhase(String xid) { // 1. 查询事务状态 TransactionState state = stateStore.getByXid(xid); if (state == null) { // 未找到记录,可能是空回滚或数据不一致 log.warn("未找到事务记录,可能已超时清理: xid={}", xid); return; } // 2. 状态验证 if (state.getStatus() == TransactionStatus.CANCELLED) { log.info("事务已取消,幂等返回: xid={}", xid); return; } if (state.getStatus() != TransactionStatus.TRYING) { throw new IllegalStateException("事务状态异常,无法取消"); } // 3. 检查是否悬挂(Try完成但未收到Confirm) if (isHanging(state)) { log.warn("检测到悬挂事务,执行补偿: xid={}", xid); compensateHanging(state); } else { // 4. 正常取消 businessService.cancelOperation(state.getBusinessId()); } // 5. 更新状态 state.setStatus(TransactionStatus.CANCELLED); state.setUpdateTime(new Date()); stateStore.update(state); } }

2.2 防悬挂锁机制

java

复制

下载

public class HangPreventionLock { private final RedissonClient redisson; private final long lockTimeout = 30; // 锁超时时间(秒) /** * 获取防悬挂锁 */ public boolean acquireHangLock(String xid, String businessId) { String lockKey = String.format("tcc:hanging:lock:%s:%s", xid, businessId); RLock lock = redisson.getLock(lockKey); try { // 尝试获取锁,最多等待5秒 return lock.tryLock(5, lockTimeout, TimeUnit.SECONDS); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return false; } } /** * Try前置检查 */ public boolean preCheckTry(String xid, String businessId) { // 1. 检查是否正在取消中 String cancelingKey = String.format("tcc:canceling:%s", xid); if (redisson.getBucket(cancelingKey).get() != null) { throw new TransactionCancellingException("事务正在取消中"); } // 2. 获取防悬挂锁 if (!acquireHangLock(xid, businessId)) { throw new TransactionLockException("获取防悬挂锁失败"); } return true; } }

篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc

需要全套面试笔记及答案
【点击此处即可/免费获取】​​​

2.3 定时清理悬挂事务

java

复制

下载

@Component public class HangTransactionCleaner { @Autowired private TransactionStateStore stateStore; @Autowired private Compensator compensator; /** * 定时清理悬挂事务(每分钟执行一次) */ @Scheduled(fixedDelay = 60000) public void cleanHangingTransactions() { // 1. 查询超时未完成的事务 List<TransactionState> hangingTransactions = stateStore.findHangingTransactions(5); // 超过5分钟 // 2. 批量处理 hangingTransactions.forEach(this::processHangingTransaction); } private void processHangingTransaction(TransactionState transaction) { try { log.info("开始处理悬挂事务: xid={}, status={}", transaction.getXid(), transaction.getStatus()); switch (transaction.getStatus()) { case TRYING: // Try成功但未收到Confirm/Cancel handleHangingTry(transaction); break; case CONFIRMING: // Confirm执行中卡住 handleHangingConfirm(transaction); break; case CANCELLING: // Cancel执行中卡住 handleHangingCancel(transaction); break; } // 3. 标记为已处理 transaction.setStatus(TransactionStatus.COMPENSATED); transaction.setUpdateTime(new Date()); stateStore.update(transaction); } catch (Exception e) { log.error("处理悬挂事务失败: xid={}", transaction.getXid(), e); // 发送告警,需要人工干预 alertService.sendAlert("悬挂事务处理失败", transaction); } } }

三、空回滚问题解决方案

3.1 Try状态检查法

java

复制

下载

@Component public class EmptyRollbackPreventer { @Autowired private TransactionLogService logService; /** * Cancel接口实现(防空回滚) */ @Transactional public void cancelWithCheck(String xid, String businessId) { // 1. 查询Try执行记录 TryRecord tryRecord = logService.getTryRecord(xid); if (tryRecord == null) { // 2. Try未执行,属于空回滚 logEmptyRollback(xid, businessId); // 3. 插入空回滚记录,防止重复处理 logService.saveEmptyRollbackRecord(xid, businessId); // 4. 直接返回成功,保证幂等性 return; } // 5. 幂等检查:是否已取消过 if (tryRecord.getStatus() == TryStatus.CANCELLED) { log.info("Cancel已执行过,幂等返回: xid={}", xid); return; } // 6. 验证状态:必须TRY_SUCCESS才能Cancel if (tryRecord.getStatus() != TryStatus.TRY_SUCCESS) { throw new IllegalStateException( String.format("事务状态[%s]不允许Cancel", tryRecord.getStatus()) ); } // 7. 执行实际的Cancel逻辑 executeCancel(businessId); // 8. 更新状态 tryRecord.setStatus(TryStatus.CANCELLED); logService.updateTryRecord(tryRecord); } /** * Try接口:必须记录状态 */ @Transactional public void tryWithRecord(String xid, String businessId) { // 1. 插入Try记录(初始状态) TryRecord record = new TryRecord(); record.setXid(xid); record.setBusinessId(businessId); record.setStatus(TryStatus.TRYING); record.setCreateTime(new Date()); logService.saveTryRecord(record); try { // 2. 执行业务Try操作 businessService.doTry(businessId); // 3. 更新为成功状态 record.setStatus(TryStatus.TRY_SUCCESS); logService.updateTryRecord(record); } catch (Exception e) { // 4. 更新为失败状态 record.setStatus(TryStatus.TRY_FAILED); record.setErrorMsg(e.getMessage()); logService.updateTryRecord(record); throw e; } } }

3.2 预写日志方案

java

复制

下载

public class PreWriteLogSolution { private final TransactionLogDAO logDAO; /** * 两阶段防空回滚方案 */ public void processTransaction(String xid, String businessId) { // 第一阶段:预写日志 preWriteTransactionLog(xid, businessId); try { // 第二阶段:执行业务 executeBusiness(xid, businessId); // 第三阶段:确认日志 confirmTransactionLog(xid); } catch (Exception e) { // 第四阶段:回滚检查 rollbackWithCheck(xid, businessId, e); } } private void preWriteTransactionLog(String xid, String businessId) { TransactionLog log = new TransactionLog(); log.setXid(xid); log.setBusinessId(businessId); log.setStatus(TransactionLogStatus.PREPARED); log.setCreateTime(System.currentTimeMillis()); log.setExpireTime(System.currentTimeMillis() + 300000); // 5分钟过期 // 插入预写日志 if (logDAO.insert(log) <= 0) { throw new TransactionException("预写事务日志失败"); } } private void rollbackWithCheck(String xid, String businessId, Exception cause) { // 1. 检查预写日志是否存在 TransactionLog preLog = logDAO.selectByXid(xid); if (preLog == null || preLog.getStatus() != TransactionLogStatus.PREPARED) { // 没有预写日志,属于空回滚或重复回滚 logEmptyRollback(xid, businessId, cause); // 插入空回滚记录 logDAO.insertEmptyRollback(xid, businessId, cause.getMessage()); return; } // 2. 执行实际的回滚逻辑 try { businessService.cancel(businessId); // 3. 更新日志状态 preLog.setStatus(TransactionLogStatus.ROLLBACKED); preLog.setUpdateTime(System.currentTimeMillis()); logDAO.update(preLog); } catch (Exception e) { log.error("回滚业务失败", e); // 标记为回滚失败,需要补偿 preLog.setStatus(TransactionLogStatus.ROLLBACK_FAILED); logDAO.update(preLog); throw e; } } }

四、综合解决方案

4.1 完整TCC事务管理器

java

复制

下载

@Slf4j @Component public class EnhancedTccTransactionManager { @Autowired private TransactionStateService stateService; @Autowired private BusinessExecutor businessExecutor; @Autowired private HangPreventionLock hangLock; /** * 增强版Try阶段 */ public void enhancedTry(String xid, String businessId, TryAction tryAction) { // 1. 防悬挂检查 checkHangingRisk(businessId); // 2. 获取分布式锁 boolean locked = hangLock.acquireHangLock(xid, businessId); if (!locked) { throw new TransactionException("获取事务锁失败"); } try { // 3. 记录开始状态 stateService.recordTryStart(xid, businessId); // 4. 执行业务Try tryAction.execute(); // 5. 记录Try成功 stateService.recordTrySuccess(xid); } catch (Exception e) { // 6. 记录Try失败 stateService.recordTryFailure(xid, e.getMessage()); throw e; } finally { // 7. 释放锁(设置较短过期时间,Confirm/Cancel会重新获取) hangLock.releaseLockWithDelay(xid, businessId, 10); } } /** * 增强版Cancel阶段 */ public void enhancedCancel(String xid, String businessId, CancelAction cancelAction) { // 1. 获取Cancel锁(防止并发Cancel) boolean cancelLocked = hangLock.acquireCancelLock(xid); try { // 2. 检查是否为空回滚 TransactionState state = stateService.getState(xid); if (state == null || state.getStatus() == TransactionStatus.INIT) { // 空回滚处理 handleEmptyRollback(xid, businessId); return; } // 3. 幂等检查 if (state.getStatus() == TransactionStatus.CANCELLED || state.getStatus() == TransactionStatus.CANCELLED_EMPTY) { log.info("Cancel已执行,幂等返回: xid={}", xid); return; } // 4. 状态验证 if (state.getStatus() != TransactionStatus.TRY_SUCCESS) { throw new IllegalStateException( String.format("事务状态[%s]不允许Cancel", state.getStatus()) ); } // 5. 执行业务Cancel cancelAction.execute(); // 6. 更新状态 stateService.markAsCancelled(xid); } finally { if (cancelLocked) { hangLock.releaseCancelLock(xid); } } } /** * 防悬挂风险检查 */ private void checkHangingRisk(String businessId) { List<TransactionState> unfinished = stateService.getUnfinishedByBusinessId(businessId, 10); // 最近10分钟 if (!unfinished.isEmpty()) { throw new TransactionHangingRiskException( String.format("业务[%s]存在未完成事务", businessId) ); } } /** * 空回滚处理 */ private void handleEmptyRollback(String xid, String businessId) { log.warn("检测到空回滚: xid={}, businessId={}", xid, businessId); // 1. 记录空回滚日志 stateService.recordEmptyRollback(xid, businessId); // 2. 监控上报 monitorService.reportEmptyRollback(xid, businessId); // 3. 返回成功(保证幂等性) } }

4.2 状态机设计

java

复制

下载

public enum TransactionStatus { // 初始状态 INIT("初始状态"), // Try阶段状态 TRYING("Try执行中"), TRY_SUCCESS("Try执行成功"), TRY_FAILED("Try执行失败"), // Confirm阶段状态 CONFIRMING("Confirm执行中"), CONFIRMED("Confirm执行成功"), CONFIRM_FAILED("Confirm执行失败"), // Cancel阶段状态 CANCELLING("Cancel执行中"), CANCELLED("Cancel执行成功"), CANCELLED_EMPTY("空回滚"), CANCEL_FAILED("Cancel执行失败"), // 悬挂处理状态 HANGING_DETECTED("检测到悬挂"), COMPENSATING("补偿中"), COMPENSATED("已补偿"), COMPENSATE_FAILED("补偿失败"); private final String description; TransactionStatus(String description) { this.description = description; } // 状态转移检查 public boolean canTransferTo(TransactionStatus target) { // 定义合法的状态转移规则 Map<TransactionStatus, Set<TransactionStatus>> transferRules = new HashMap<>(); transferRules.put(INIT, Set.of(TRYING)); transferRules.put(TRYING, Set.of(TRY_SUCCESS, TRY_FAILED, HANGING_DETECTED)); transferRules.put(TRY_SUCCESS, Set.of(CONFIRMING, CANCELLING, HANGING_DETECTED)); transferRules.put(TRY_FAILED, Set.of(CANCELLING)); // ... 其他转移规则 Set<TransactionStatus> allowedTargets = transferRules.get(this); return allowedTargets != null && allowedTargets.contains(target); } }

五、数据库表设计

5.1 事务状态表

sql

复制

下载

CREATE TABLE tcc_transaction_state ( id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键', xid VARCHAR(64) NOT NULL COMMENT '全局事务ID', business_id VARCHAR(64) NOT NULL COMMENT '业务ID', business_type VARCHAR(32) NOT NULL COMMENT '业务类型', -- 状态信息 status VARCHAR(32) NOT NULL COMMENT '事务状态', retry_count INT DEFAULT 0 COMMENT '重试次数', max_retry INT DEFAULT 3 COMMENT '最大重试次数', error_msg TEXT COMMENT '错误信息', -- 时间信息 create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', expire_time DATETIME NOT NULL COMMENT '过期时间', finish_time DATETIME COMMENT '完成时间', -- 控制字段 version INT DEFAULT 1 COMMENT '版本号(乐观锁)', is_deleted TINYINT DEFAULT 0 COMMENT '是否删除', -- 索引 UNIQUE KEY uk_xid (xid), KEY idx_business_id (business_id), KEY idx_status_expire (status, expire_time), KEY idx_create_time (create_time) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='TCC事务状态表';

5.2 防悬挂锁表

sql

复制

下载

CREATE TABLE tcc_hang_lock ( id BIGINT PRIMARY KEY AUTO_INCREMENT, lock_key VARCHAR(128) NOT NULL COMMENT '锁Key', lock_value VARCHAR(64) NOT NULL COMMENT '锁值', lock_holder VARCHAR(64) NOT NULL COMMENT '锁持有者', expire_time DATETIME NOT NULL COMMENT '锁过期时间', create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_lock_key (lock_key), KEY idx_expire_time (expire_time) ) COMMENT='防悬挂锁表';

5.3 空回滚记录表

sql

复制

下载

CREATE TABLE tcc_empty_rollback_log ( id BIGINT PRIMARY KEY AUTO_INCREMENT, xid VARCHAR(64) NOT NULL COMMENT '事务ID', business_id VARCHAR(64) NOT NULL COMMENT '业务ID', reason VARCHAR(256) COMMENT '空回滚原因', create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_xid_business (xid, business_id), KEY idx_create_time (create_time) ) COMMENT='空回滚记录表';

篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc

需要全套面试笔记及答案
【点击此处即可/免费获取】​​​

六、监控与告警

6.1 监控指标

java

复制

下载

@Component public class TccMonitor { // 悬挂事务监控 @Meter("tcc.hanging.count") private Meter hangingCount; // 空回滚监控 @Meter("tcc.empty_rollback.count") private Meter emptyRollbackCount; // 事务成功率监控 @Timer("tcc.transaction.duration") private Timer transactionDuration; // 记录悬挂事务 public void recordHanging(String xid, String businessId) { hangingCount.increment(); // 记录详细日志 log.warn("检测到悬挂事务: xid={}, businessId={}", xid, businessId); // 发送监控事件 eventPublisher.publishEvent(new HangingTransactionEvent(xid, businessId)); } // 记录空回滚 public void recordEmptyRollback(String xid, String businessId) { emptyRollbackCount.increment(); log.info("记录空回滚: xid={}, businessId={}", xid, businessId); // 上报到监控系统 monitorClient.reportEmptyRollback(xid, businessId); } }

6.2 告警规则配置

yaml

复制

下载

# alert-rules.yml tcc_monitoring: rules: # 悬挂事务告警 - alert: HangingTransactionDetected expr: | increase(tcc_hanging_count_total[5m]) > 10 for: 2m labels: severity: warning annotations: summary: "检测到悬挂事务" description: "5分钟内新增悬挂事务 {{ $value }} 个" # 空回滚率过高告警 - alert: HighEmptyRollbackRate expr: | rate(tcc_empty_rollback_count_total[10m]) / rate(tcc_transaction_total[10m]) > 0.01 for: 5m labels: severity: warning annotations: summary: "空回滚率过高" description: "空回滚率达到 {{ $value | humanizePercentage }}" # 事务成功率下降告警 - alert: TransactionSuccessRateDrop expr: | (rate(tcc_success_count_total[10m]) / rate(tcc_transaction_total[10m])) < 0.95 for: 3m labels: severity: critical annotations: summary: "事务成功率下降" description: "事务成功率下降至 {{ $value | humanizePercentage }}"

七、最佳实践总结

7.1 悬挂问题防护要点

text

复制

下载

✅ Try前检查:检查同一业务是否存在未完成事务 ✅ 资源锁:使用分布式锁控制并发 ✅ 超时清理:定时任务清理超时事务 ✅ 状态验证:Cancel前验证事务状态

7.2 空回滚问题防护要点

text

复制

下载

✅ Try记录:必须记录Try执行状态 ✅ Cancel检查:Cancel前检查Try是否执行 ✅ 幂等设计:Cancel操作必须幂等 ✅ 空回滚日志:记录空回滚用于分析

7.3 实施建议

java

复制

下载

// 分阶段实施路线图 public class TccSolutionRoadmap { // Phase 1: 基础防护(1-2周) // - 实现Try状态记录 // - Cancel幂等性保证 // - 基本状态检查 // Phase 2: 悬挂防护(2-3周) // - 引入防悬挂锁 // - 实现超时清理 // - 添加监控告警 // Phase 3: 优化完善(3-4周) // - 空回滚自动处理 // - 性能优化 // - 可视化监控 // Phase 4: 高级特性(4-6周) // - 机器学习预测 // - 自适应调整 // - 多级降级策略 }

通过以上综合解决方案,可以有效预防和解决TCC事务中的悬挂和空回滚问题,保障分布式事务的可靠性和数据一致性。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询