SQL 分布式事务处理方法解析

1次阅读

mysql xa在分库场景基本不可用,因协调者崩溃后无法全局判断分支状态,且各节点版本、binlog、时钟等不一致导致xa commit失败;seata at需手动建全字段undo_log表;最终一致性方案更可控。

SQL 分布式事务处理方法解析

MySQL XA 事务为什么在分库场景下基本不可用

XA 协议理论上支持跨库两阶段提交,但实际在分库(比如 ShardingSphere 或自研分片)中几乎无法稳定落地。根本原因不是协议本身错,而是协调者(TM)和参与者(RM)的故障恢复能力严重不对等。

  • MySQL 的 XA RECOVER 只能查出本实例上未完成的 XA 分支,一旦 coordinator 崩溃,没有全局视图,无法判断哪些分支该 commit、哪些该 rollback
  • 分库环境下,不同物理库的 MySQL 版本、binlog 格式、server_id 甚至时钟可能不一致,导致 XA PREPARE 成功后,某节点在崩溃恢复时拒绝 XA COMMIT
  • ShardingSphere 等中间件默认关闭 XA 支持,开启后需额外部署 Seata ATAtomikos,但它们对 MySQL 的 XA 日志解析有版本限制(例如不兼容 MySQL 8.0.33+ 的 redo log 优化)

Seata AT 模式下 undo_log 表必须手动建且字段不能少

Seata 的 AT 模式靠本地 undo_log 表记录回滚日志,但它不会自动建表,也不校验表结构。很多团队上线后才发现事务卡住或回滚失败,根源是字段缺失或类型不匹配。

  • 必须包含 branch_id(BIGINT NOT NULL)、xid(VARCHAR(128) NOT NULL)、context(VARCHAR(128) NOT NULL)、rollback_info(LONGBLOB NOT NULL)、log_status(TINYINT NOT NULL)、log_created(DATETIME NOT NULL)、log_modified(DATETIME NOT NULL)
  • rollback_info 类型必须是 LONGBLOB,用 TEXT 会导致大事务序列化失败,错误信息类似 java.io.EOFException: Unexpected end of ZLIB input stream
  • 每个分库都要建同名同结构的 undo_log 表,Seata 不支持跨库路由该表

本地消息表 + 最终一致性比强一致更可控

真正线上扛量的分布式事务,90% 以上走的是“可靠消息 + 对账补偿”,而不是试图用框架模拟 ACID。它不依赖数据库层的复杂协调,出问题时可查、可重试、可人工介入。

  • 消息表必须和业务表在同一库同一事务中写入,用 INSERT INTO msg_table (...) VALUES (...) + UPDATE biz_table SET status = 'sent' WHERE ... 包在同一个 BEGIN/COMMIT
  • 投递服务轮询时要用 select ... for UPDATE SKIP LOCKED,避免多个 worker 重复取同一条消息;更新状态时要带条件,如 UPDATE msg_table SET status = 'sending' WHERE id = ? AND status = 'ready'
  • 不要把重试逻辑全交给 MQ(比如 rabbitmq 的死信队列),它的重试间隔不可控,且无法关联原始业务上下文;应由独立的调度服务管理重试次数、退避策略和告警阈值

TCC 接口的 Confirm/Cancel 必须幂等且不依赖 try 阶段状态

TCC 是最灵活也最容易翻车的模式。很多人以为只要三个接口写出来就完事了,结果压测时大量 Confirm 失败,日志里反复出现 no try record found for xid

  • ConfirmCancel 必须设计成纯幂等操作:比如扣库存用 UPDATE stock SET qty = qty - ? WHERE sku_id = ? AND qty >= ?,而不是先 SELECT 再判断
  • 不能假设 Try 一定成功或已落库——网络超时、应用重启都可能导致 Try 执行了但没返回响应,此时 Seata 会直接发 Confirm,你的接口必须能凭 xid 和业务参数自行判断是否该执行
  • Cancel 不是 Try 的逆操作,而是“确保最终不发生”。例如 Try 预占额度,Cancel 就是释放预占,哪怕预占记录已丢失,也要确保用户额度不被多扣

分布式事务真正的难点从来不在怎么写代码,而在于怎么定义“事务边界”——哪些操作必须原子,哪些可以妥协,以及当系统部分失联时,你愿意接受多久的数据不一致。这些决策没法靠框架自动做。

text=ZqhQzanResources