iris.database() 是空壳,无法直连 mysql;必须用 database/sql + go-sql-driver/mysql 创建 *sql.db,调用 ping() 验证连接,合理配置连接池,并通过 iris.context.values() 注入使用。

iris.Database() 不能直接连 MySQL
iris 框架本身不内置数据库驱动,iris.Database() 是个空壳,调用它会 panic 或返回 nil。你真正需要的是用 Go 原生的 database/sql 配合 MySQL 驱动(比如 github.com/go-sql-driver/mysql)来建连接池,再把它挂到 iris 的容器里供 handler 使用。
常见错误现象:panic: runtime error: invalid memory address or nil pointer dereference,往往是因为没手动初始化 db 实例就直接用了 ctx.Values().Get("db") 之类逻辑。
- 先
go get github.com/go-sql-driver/mysql - 用
sql.Open("mysql", dsn)创建 *sql.DB,不是 iris 自带的 “Database” - 务必调用
db.Ping()验证连接,否则可能延迟到第一次查询才暴露认证失败或网络问题 - 设置
db.SetMaxOpenConns()和db.SetMaxIdleConns(),避免高并发下连接耗尽(默认最大 0,即无限制,容易打爆 MySQL)
把 *sql.DB 注入到 iris.Context 最安全的方式
别用全局变量存 *sql.DB,iris 推荐通过 app.UseRouter() + ctx.Values().Set() 在路由中间件中注入,这样每个请求都能拿到同一实例,又不会污染包级作用域。
示例代码片段:
db, err := sql.Open("mysql", "user:pass@tcp(127.0.0.1:3306)/mydb?parseTime=true&loc=Local")
if err != nil {
log.Fatal(err)
}
if err = db.Ping(); err != nil {
log.Fatal(err)
}
app.UseRouter(func(ctx iris.Context) {
ctx.Values().Set("db", db)
ctx.Next()
})
使用时在 handler 里取:db := ctx.Values().Get("db").(*sql.DB)。注意类型断言要加括号,否则编译报错。
- 不要在 handler 内部重复
sql.Open—— 每次都新建连接池,资源泄漏 - 如果项目分模块,可封装一个
GetDB(ctx iris.Context) *sql.DB工具函数,避免到处写类型断言 - 若用依赖注入框架(如 wire),也可提前构造好 db 实例并传入 app 初始化函数,但对小项目略重
事务处理必须显式控制,iris 不自动管理
iris 不提供类似 Django ORM 的自动事务包装,所有事务都要靠 db.Begin()、tx.Commit()、tx.Rollback() 手动完成。常见坑是:忘了 rollback,或 panic 后没 recover 导致连接卡死。
推荐写法(带 defer + recover):
func handleOrder(ctx iris.Context) {
db := ctx.Values().Get("db").(*sql.DB)
tx, err := db.Begin()
if err != nil {
ctx.StatusCode(500)
ctx.WriteString("tx begin failed")
return
}
defer func() {
if r := recover(); r != nil {
tx.Rollback()
panic(r)
}
}()
_, err = tx.Exec("INSERT INTO orders (...) VALUES (...)")
if err != nil {
tx.Rollback()
ctx.StatusCode(500)
ctx.WriteString("insert failed")
return
}
err = tx.Commit()
if err != nil {
ctx.StatusCode(500)
ctx.WriteString("commit failed")
return
}
}
- 不要在事务里调用
db.QueryRow()等非 tx 方法,它们走的是独立连接,不参与当前事务 - 事务内所有 SQL 必须用
tx.Query()/tx.Exec(),不能混用db实例 - 长事务会占用连接,MySQL 默认
wait_timeout=28800(8 小时),但业务层应尽量缩短事务时间
DSN 字符串里 time_zone 和 parseTime 是高频翻车点
MySQL 默认时区是 SYSTEM,Go 的 time.Time 解析时间字段(如 DATETIME)时若没配对,会出现时间偏移 8 小时(本地 vs UTC)、解析失败或空值。
正确 DSN 示例:user:pass@tcp(127.0.0.1:3306)/mydb?parseTime=true&loc=Local
-
parseTime=true是必须的,否则sql.Scan到time.Time会报can't scan into dest -
loc=Local表示用本机时区解析;若 MySQL server 用 UTC,这里应写loc=UTC - 避免用
loc=Asia/Shanghai—— 容器或不同服务器时区配置不一致时行为不可控 - 如果表里有
TIMESTAMP字段,且 MySQL server 开启了explicit_defaults_for_timestamp,也要确认是否需额外加autoCommit=true
最易被忽略的是:开发本地跑得通,上线后因服务器时区为 UTC 而时间全错,而且错误不报 panic,只静默偏差。











