mysql原生xa不能算强一致,因其缺乏可靠协调器、无自动故障恢复、prepared状态长期锁表;seata at模式需严格配置undo_log表、store.mode和datasourceproxy;binlog+kafka方案依赖row格式与幂等设计实现最终一致。

BEGIN/COMMIT 就不生效;XA START 等命令虽存在,但因缺乏可靠协调器、无自动故障恢复、PREPARED 状态长期锁表等致命缺陷,线上不可用。
为什么 MySQL 原生 XA 在跨库场景下不能算“强一致”
很多人以为开了 innodb_support_xa=ON(8.0.30+ 已默认关闭)就能用 XA 保证强一致,实际落地会立刻暴露问题:
-
XA RECOVER返回的 xid 是二进制 blob,不含业务单号、时间戳、分支信息,崩溃后无法判断该XA COMMIT还是XA ROLLBACK - 一个卡在
PREPARED状态的 XA 事务,会持续持有行锁和连接,MySQL 不会自动清理,DBA 必须人工介入,线上不敢依赖 - 跨实例时,MySQL 不提供全局事务 ID 分发、状态持久化、网络失败重试机制——这些都得应用层自己补,等于重写一套事务管理器
-
XA PREPARE后若协调者宕机,所有参与者处于“悬挂”状态,既不能提交也不能回滚,系统进入灰色故障区
Seata AT 模式接入时最常踩的三个配置坑
Seata 是目前最接近“开箱即用”的跨库强一致替代方案,但它不是 MySQL 的一部分,而是独立中间件。配错以下任意一项,整个事务链路就退化为多个孤立本地事务:
- 每个物理库(注意:不是逻辑库名,而是每个
jdbc:mysql://host:port/db_name对应的真实库)都必须手动执行官方 DDL 创建undo_log表,字段名大小写、索引(特别是branch_id上的唯一索引)缺一不可 -
seata-server的store.mode必须设为db并指向一个高可用数据库,否则分支注册状态丢失,服务重启后无法恢复事务上下文 - 应用端数据源必须被 Seata 的
DataSourceProxy包装(不是简单加个 @Transactional),否则 SQL 不会被拦截,before_image/after_image根本不记录
binlog + Kafka 实现最终一致性的最小可行路径
当业务能接受秒级延迟、且对资金类操作有补偿兜底能力时,这是线上故障率最低、运维成本最小的方案。它不追求“强一致”,但胜在可控、可观测、可修复:
- 业务库执行本地事务(如扣减库存),成功后立即
COMMIT,不等待下游 - 用
canal或maxwell监听 binlog,过滤出目标表的UPDATE/INSERT事件;注意:binlog_format必须为ROW,否则NOW()、UUID()等函数会导致下游解析错乱 - 消息体必须包含完整变更前后的主键 + 字段值,并带全局唯一 trace_id;下游消费时用
INSERT ... ON DUPLICATE KEY UPDATE或先SELECT FOR UPDATE判断是否已处理,确保幂等 - 消息发送失败不可丢,推荐“本地消息表 + 定时扫描”模式:把消息写入同一库的
msg_outbox表,再用定时任务异步发 Kafka,失败则重试并告警











