TiDB 在 Golang 微服务中需严格对齐分布式语义:连接字符串必须加 ?timezone=UTC&parseTime=true,事务须显式控制并传入带超时的 context,批量写入要按 Region 分片,避免主键单调递增导致热点。

TiDB 在 Golang 微服务中不是“引入一个库”就能用的数据库,它本质是一套分布式系统,强一致性依赖 PD + TiKV + TiDB Server 三组件协同。直接拿 MySQL 驱动连上就写代码,大概率踩坑——事务超时、时间戳不一致、Region 打散失败、甚至出现幻读。
连接字符串里必须包含 timezone=UTC
Golang 的database/sql 驱动(如 github.com/go-sql-driver/mysql)默认会读取本地时区,而 TiDB 的 TSO 时间戳由 PD 统一授时,单位是纳秒级 Unix 时间戳,不带时区偏移。如果客户端时区非 UTC,now()、timestamp 字段插入/查询会出现偏差,导致事务内时间判断错乱,MVCC 版本误判。
- 必须在 DSN 后显式加上
?timezone=UTC - 不要依赖
time.Local构造time.Time值传给Exec或Query - ORM(如 GORM)需全局设置
driver.Config.Loc = time.UTC,否则CreatedAt字段可能被自动转成本地时间再发给 TiDB
错误示例:root:@tcp(127.0.0.1:4000)/test → 正确示例:root:@tcp(127.0.0.1:4000)/test?timezone=UTC&parseTime=true
事务必须显式控制,不能依赖 defer rollback
TiDB 的两阶段提交(2PC)耗时比单机 MySQL 长得多,尤其跨 Region 场景下,COMMIT 可能卡在 Prepare 阶段几十毫秒。Golang 中常见错误是:
用
defer tx.Rollback()包裹整个事务逻辑,但没加if tx == nil判断-
tx.Commit()失败后未检查 error,直接 return,导致连接泄漏或脏数据残留立即学习“go语言免费学习笔记(深入)”;
在事务内调用其他 service 方法,而该方法又开了新事务(嵌套事务不成立,TiDB 不支持 SAVEPOINT 级别嵌套)
总是先
err := tx.Commit(),成功才 return;失败则tx.Rollback()并返回 error不要用
defer自动 rollback,除非你确认tx一定非 nil 且只在函数出口 rollback事务内避免调用外部 HTTP/gRPC,防止阻塞时间过长触发
tidb_txn_mode = pessimistic下的锁等待超时(默认 1s)
批量写入必须按 Region 分片,否则性能断崖下跌
TiDB 写入压力集中在 Leader Region 节点,如果一次性 INSERT INTO user VALUES (...), (...), ... 插入 1000 行,但这些行的 id 是连续递增主键,它们全落在同一个 Region,就会打满单个 TiKV 实例。
- 主键尽量用
BIGINT UNSIGNED+auto_random,或业务层生成雪花 ID(int64),避免单调递增 - 批量导入前,用
SHOW TABLE REGIONS FROM test.user查看当前 Region 分布 - 写入前按
id % N或哈希分组,N 至少等于 TiKV 节点数,每组单独Exec - 千行以上批量操作,优先用
LOAD DATA或TiDB Lightning工具,绕过 SQL 层解析开销
Context 超时必须覆盖整个事务生命周期
TiDB 默认事务超时是 tidb_txn_mode = optimistic 下的 10 分钟(tidb_session_timeout),但微服务场景下不该依赖这个兜底。网络抖动、PD 响应延迟、TiKV GC 延迟都可能导致单个事务卡住。
- 创建
sql.Tx时必须传入带 timeout 的context.Context,例如ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) -
db.BeginTx(ctx, nil)的 ctx 会传递给后续所有tx.Query/Exec调用 - 如果事务内有重试逻辑(比如幂等写入),每次重试都要 new 一个 clean context,不能复用旧 ctx
- 注意:
context.WithTimeout的 deadline 是从调用时刻起算,不是从 Commit 开始,所以预留时间要包含网络往返 + TiKV 处理 + PD 授时
真正麻烦的不是连上 TiDB,而是让 Golang 微服务的行为和 TiDB 的分布式事务语义对齐——时间、分片、超时、错误处理,每个环节松一点,强一致性就变成“看起来一致”。


















