beego直连tidb需配置dsn添加timezone=utc和parsetime=true以防时间静默偏移,事务须用begintx配合isretryableerror重试,批量插入禁用单条on duplicate key update,json/enum字段需自定义scan处理。

Beego 能直接连 TiDB,但默认配置会出问题:时间错乱、事务失败、批量插入慢、JSON 字段 panic——必须改 DSN 和事务写法。
DSN 必须加 timezone=UTC 和 parseTime=true
TiDB 的 TSO 时间戳是 UTC 纳秒级,而 Beego 默认用本地时区解析 DATETIME/TIMESTAMP。不加这两个参数,time.Time 字段会静默偏移几小时,MVCC 版本错乱、事务判断失效都可能发生。
-
timezone=UTC强制驱动按 UTC 解析所有时间类型,与 PD 授时对齐 -
parseTime=true让 MySQL 驱动把时间类型转成time.Time,否则 Scan 到 struct 会 panic 或返回空字符串 - 别写
loc=Asia%2FShanghai或loc=Local——和timezone=UTC冲突,TiDB 不依赖客户端时区做计算 - 完整示例 DSN:
root:@tcp(127.0.0.1:4000)/test?charset=utf8mb4&timezone=UTC&parseTime=true&clientFoundRows=true
事务必须用 BeginTx 并检查 IsRetryableError
Beego 的 ORM 默认调用 db.Begin(),但 TiDB 是乐观事务模型,冲突错误(如 ErrWriteConflict)只在 Commit() 阶段抛出。db.Begin() 没有上下文支持,无法重试,容易丢数据或 panic。
- 用
db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelRepeatableRead})替代db.Begin() - 捕获错误后,用
errors.IsRetryableError(err)(来自github.com/pingcap/tidb/util/errors)判断是否该重试 - 别依赖
err == sql.ErrTxDone判定事务状态——TiDB 可能在 Commit 后才报错 - Beego 中手动管理事务时,
defer tx.Rollback()要包在闭包里,并在Commit()成功后置tx = nil,防止重复 rollback
批量插入必须用多值语句,禁用 ON DUPLICATE KEY UPDATE
TiDB 分布式架构下,单条 INSERT 触发多次 RPC 和时间戳分配,吞吐极低。Beego ORM 的 InsertMulti 默认生成多值语句,但要注意限制行数和字段类型。
- 单次插入不超过 1000 行(受
tidb-max-batch-size和内存影响) - 避免用
ON DUPLICATE KEY UPDATE:TiDB 5.0+ 支持,但会退化为悲观锁行为,严重拖慢并发 - 推荐先
SELECT ... FOR UPDATE再判存否;若必须流式写入,可在 DSN 加&tidb-batch-insert=1启用自动分批(仅对简单 INSERT 生效) - Beego 中慎用
o.Insert(&u)循环插入,改用o.Raw("INSERT INTO ... VALUES (?, ?), (?, ?)").Exec()手拼多值
JSON 和 ENUM 字段不能直接 Scan 到 string 或 sql.NullString
TiDB 的 JSON 和 ENUM 类型在 Go 中无法被标准 sql.Scanner 正确处理 NULL 值。Beego ORM 直接 Scan 到 string 会报 sql: Scan error on column index X;用 sql.NullString 也常解析失败。
- JSON 字段建议定义为
json.RawMessage,再用json.Unmarshal解析 - ENUM 字段建议映射为
string+ 自定义Scan方法,显式处理nil和空字符串 - Beego 的
orm.RegisterModel不支持自定义 Scanner,需绕过 ORM,用o.Raw(...).QueryRow().Scan(&v)手动控制 - 如果必须用 ORM,可将 JSON/ENUM 字段设为
orm:"column(xxx);null",并在结构体中配func (u *User) Scan(value interface{}) error实现安全转换
最易被忽略的是时区和事务重试机制——它们不会立刻报错,而是让时间字段悄悄漂移、让并发更新偶尔丢失。一旦上线,这类问题极难复现和定位。











