Seata-go 是原生 Go 实现的分布式事务客户端,需配合独立部署的 Java 版 seata-server(TC)运行,不支持单进程启动;必须版本匹配(如 server 2.0.0 对应 seata-go 2.0.0),且 AT 模式要求 DB 有 undo_log 表、数据源须用 NewDataSourceProxy 包装、事务对象不可跨 goroutine 复用。

Seata-go 不是“用 Go 调用 Java 版 Seata”,而是原生 Go 实现的分布式事务客户端,它必须配合独立部署的 seata-server(Java 进程)运行。直接在 Go 里“单进程启动 Seata”不可行,这点常被初学者误判。
seata-server 必须独立部署,且版本需与 seata-go 兼容
-
seata-server是中心化协调器(TC),Go 应用只是客户端(RM/TM),二者通过 gRPC 或 HTTP 通信 - 当前稳定搭配是:
seata-server:2.0.0+seata.apache.org/seata-go@2.0.0 - 若用
seata-server:1.8.x,seata-go需降级到@v1.8.0,否则会报unknown message type或连接后立即断连 - Docker 启动示例:
docker run -d \ --name seata-server \ -p 8091:8091 -p 7091:7091 \ -e SEATA_IP=127.0.0.1 \ -e STORE_MODE=file \ seataio/seata-server:2.0.0
- 注意:
SEATA_IP必须设为宿主机可访问的地址(K8s 环境填 Service 名),不能留空或写localhost
tm.Begin() 的上下文和生命周期必须手动管理
tm.Begin(ctx)返回的*globaltransaction.GlobalTransaction不是线程安全对象,不能跨 goroutine 复用常见错误:在 HTTP handler 中调用
Begin(),但把事务对象传给异步 goroutine 执行 Confirm/Cancel → 导致tx.Commit()无响应或 panic-
正确做法:
- 在入口(如 Gin handler)调用
tm.Begin() - 用
defer绑定Rollback(),并在业务逻辑结束后显式Commit() - 所有分支操作(如调用 serviceA/serviceB)必须在同一个 goroutine 内完成,或通过 context 透传事务 ID(非事务对象本身)
- 在入口(如 Gin handler)调用
-
示例关键片段:
func CreateOrder(c *gin.Context) { ctx := c.Request.Context() tx, err := tm.Begin(ctx) if err != nil { c.JSON(500, err) return } defer func() { if r := recover(); r != nil { tx.Rollback() } }() <p>if err := serviceA.Transfer(ctx, tx.XID()); err != nil { tx.Rollback() return } if err := serviceB.Deduct(ctx, tx.XID()); err != nil { tx.Rollback() return } tx.Commit() // 显式提交,不依赖 defer }
AT 模式下,每个 DB 表必须有 undo_log 表,且 DML 必须走 Seata 代理
-
seata-go的 AT 模式依赖自动快照 +undo_log回滚,不是靠数据库 MVCC 或 binlog - 每个参与事务的 MySQL 数据库(哪怕只有一个)都必须建表:
CREATE TABLE `undo_log` ( `id` bigint NOT NULL AUTO_INCREMENT, `branch_id` bigint NOT NULL, `xid` varchar(100) NOT NULL, `context` varchar(128) NOT NULL, `rollback_info` longblob NOT NULL, `log_status` int NOT NULL, `log_created` datetime NOT NULL, `log_modified` datetime NOT NULL, `ext` varchar(100) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`) ) ENGINE=InnoDB;
- 数据源初始化时,必须用
seata.NewDataSourceProxy()包装原始*sql.DB,否则所有 SQL 都不会被拦截,undo_log不写入,回滚失效 - 错误写法:
db, _ := sql.Open(...); db.Exec("UPDATE ...")→ 完全绕过 Seata - 正确写法:
db, _ := sql.Open(...) proxyDB := seata.NewDataSourceProxy(db) _, _ = proxyDB.ExecContext(ctx, "UPDATE account SET balance = balance - ? WHERE user_id = ?", amount, userID)
AT 模式看似透明,但 undo_log 表缺失、数据源未代理、server 版本错配这三点,占了线上事务静默失败的 90% 以上。别跳过验证步骤——启动后立刻查 undo_log 是否有记录,比看日志更直接。


















