sqlite在gin中卡顿的根本原因是未启用wal模式且连接池配置不当,必须在dsn中添加_journal_mode=wal&_busy_timeout=5000,并合理设置setmaxopenconns(5)和setmaxidleconns(2),禁用连接老化,避免长事务,才能实现高并发写入稳定运行。

SQLite 在 Gin 中跑得起来,但默认配置下容易卡住、并发写入失败、文件锁报错——根本原因不是 Gin 本身,而是 database/sql 连接池和 SQLite 的 WAL 模式没调对。
为什么 sql.Open("sqlite3", "xxx.db") 连上就报 busy 错误?
SQLite 默认使用 rollback journal,写操作会独占整个数据库文件,多个请求同时写(比如并发 POST)必然触发 database is locked。这不是 Gin 的问题,是驱动没启用 WAL 模式 + 连接池没设超时。
- 必须在 DSN 后加
?_journal_mode=WAL&_busy_timeout=5000,例如:"./data/app.db?_journal_mode=WAL&_busy_timeout=5000" -
_busy_timeout单位是毫秒,低于 1000 在高并发下基本没用;设到 5000 是底线,再高不如优化逻辑 - WAL 模式允许读写并发,但要求 SQLite 版本 ≥ 3.7.0(Go-sqlite3 v1.14+ 自带,不用额外装)
- 别信“加了 WAL 就万事大吉”——如果事务里做大量写且不及时
Commit(),照样锁表
用 db.SetMaxOpenConns(1) 真的合理吗?
网上很多教程直接设成 1,说“SQLite 不支持多连接”,这是过时认知。现代 go-sqlite3 驱动支持多连接 + WAL,设 1 反而成为性能瓶颈,尤其在 Gin 的 HTTP handler 并发场景下。
- 推荐值:
db.SetMaxOpenConns(5)—— 够用且避免争抢;db.SetMaxIdleConns(2)防止空闲连接堆积 - 设太高(如 20+)没意义:SQLite 文件级锁本质没变,只是把等待从应用层推到驱动层,错误更难定位
- 真正要限制的是单个请求内的事务时长,不是连接数;长事务比连接数更容易引发 busy
- 务必调用
db.SetConnMaxLifetime(0)(禁用连接老化),SQLite 连接不需要重连
Gin 中如何安全注入 *sql.DB 实例?
别在 handler 里每次 sql.Open,也别用全局变量裸奔。Gin 的 c.MustGet("db") 是最轻量又可控的方式。
- 启动时创建
*sql.DB,存进 Gin 的Engine.Context全局键:r.Use(func(c *gin.Context) { c.Set("db", db); c.Next() }) - handler 里取:
db, ok := c.MustGet("db").(*sql.DB),注意类型断言失败 panic,建议封装工具函数 - 不要把
*sql.DB塞进结构体再传给 handler——Gin 的 context 机制已经够用,多一层抽象反而绕晕 - 如果用了 GORM,同样走
c.Set("gorm", db),但注意 GORM v2 默认开启 prepare stmt,SQLite 下可能触发 unexpected prepare 错误,需关掉:.Set("gorm:prepare_stmt", false)
事务嵌套失败,sql.Tx 提交后查不到数据?
SQLite 不支持真正的嵌套事务(SAVEPOINT 是模拟),Gin handler 里手动 Begin() + Commit() 后查不到刚写的数据,大概率是没用同一个 *sql.Tx 实例,或者忘了 defer tx.Rollback() 导致资源泄漏。
- 必须用
tx, err := db.Begin()创建事务,所有tx.Query/Exec才走同一上下文;用db.Query就脱离事务了 - 常见陷阱:在事务内调另一个函数,那个函数又去
db.Query—— 它查的是未提交快照外的数据 - SQLite 的
READ UNCOMMITTED隔离级别下,其他连接能看到未提交数据,但 Gin 默认每个请求独立连接,所以你看不到 - 简单 CRUD 场景尽量不用显式事务;真需要,用
defer func() { if r := recover(); r != nil { tx.Rollback() } }()保底回滚
SQLite 跑在 Gin 里最麻烦的从来不是语法或连接,而是对“单文件数据库”的并发预期管理——它不拒绝并发,但拒绝无序的长事务和裸写的连接池配置。调对 WAL、控好连接数、管住事务边界,剩下的就是写 SQL 本身了。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











