SQLite是文件级嵌入式数据库,无服务端进程,不支持TCP连接管理,其驱动未实现连接复用逻辑,故SetMaxOpenConns等连接池参数为空实现、完全无效;真正影响并发的是journal_mode、busy_timeout等PRAGMA设置。

SQLite 不支持传统意义上的连接池,GORM 中对 SQLite 调用 SetMaxOpenConns 等方法无效,强行设置不会报错但完全不生效。
为什么 SQLite 的连接池配置被忽略?
SQLite 是文件级嵌入式数据库,没有服务端进程,所有操作都在进程内完成。它通过文件锁(如 database is locked)控制并发,而非 TCP 连接管理。因此 *sql.DB 提供的连接池参数(SetMaxOpenConns、SetMaxIdleConns、SetConnMaxLifetime)在 SQLite 驱动中是空实现——调用它们既不报错,也不起作用。
- 你看到
sqlDB, err := db.DB()成功返回,不代表后续池参数已生效 - SQLite 驱动(
gorm.io/driver/sqlite)底层用的是github.com/mattn/go-sqlite3或纯 Go 实现(github.com/glebarez/sqlite),二者均未实现连接复用逻辑 - 每次
db.Create()或db.First()都可能打开/关闭文件句柄,但不是“从池中取连接”
SQLite 项目里真正影响并发行为的关键配置
替代连接池的是 SQLite 自身的锁模式与 pragma 设置,这些才决定写入吞吐和阻塞表现:
-
sqlite.Open("app.db?_journal_mode=WAL&_busy_timeout=5000"):开启 WAL 模式可允许多读一写并发;_busy_timeout控制等待锁的最长时间(单位毫秒) - 必须在
gorm.Open后立即执行db.Exec("PRAGMA journal_mode = WAL")和db.Exec("PRAGMA busy_timeout = 5000"),否则 URL 参数可能被忽略(尤其在某些驱动版本中) - 外键约束默认关闭:
db.Exec("PRAGMA foreign_keys = ON")必须手动开启,否则关联逻辑失效 - 内存数据库
:memory:每次sqlite.Open都是全新实例,无法共享状态,不适合多 goroutine 共用
开发阶段如何避免 “database is locked” 报错
这不是 GORM 的 bug,而是 SQLite 文件锁机制在高并发写场景下的正常反馈:
- 避免长事务:不要在事务里做 HTTP 请求、文件读写等耗时操作
- 拆分批量写入:用
db.CreateInBatches()替代循环单条Create(),减少锁持有次数 - 检查是否多个进程(或多个测试用例)同时写同一个 DB 文件——SQLite 不支持跨进程写,
os.OpenFile权限错误也会导致此报错 - 若用
github.com/glebarez/sqlite(纯 Go 驱动),注意其 WAL 支持不如 CGO 版本稳定,生产环境建议优先用gorm.io/driver/sqlite
SQLite 的“连接管理”本质是文件 I/O 控制和 pragma 协调,不是配置数字参数就能解决的。真正要调的从来不是 SetMaxOpenConns,而是 _journal_mode、_busy_timeout 和事务粒度。漏掉 pragma 初始化或误以为连接池参数有效,是线上卡顿最常被忽略的两个点。











