
在 Go 中使用 sqlite3 驱动进行多 goroutine 访问时,应为每个 goroutine 分配独立的 database/sql 连接,并启用 cache=shared 和 mode=rwc 参数优化并发性能;驱动底层自动处理锁等待与重试,无需手动加锁。
在 go 中使用 sqlite3 驱动进行多 goroutine 访问时,应为每个 goroutine 分配独立的 database/sql 连接,并启用 `cache=shared` 和 `mode=rwc` 参数优化并发性能;驱动底层自动处理锁等待与重试,无需手动加锁。
SQLite 是一个嵌入式数据库,其并发模型与服务端数据库(如 PostgreSQL 或 MySQL)有本质区别:它基于文件锁实现并发控制,不支持真正的多写并行。当多个 goroutine 通过同一个 *sql.DB 实例执行写操作(INSERT/UPDATE/DELETE)时,若未合理配置,极易触发 database is locked 错误。
✅ 正确做法是:复用 *sql.DB 实例,但确保每个活跃 goroutine 使用独立的 *sql.Conn 或通过 db.Exec() / db.Query() 等方法获取临时连接。database/sql 包内置连接池,默认会为并发操作自动分配/复用底层 SQLite 连接(即 sqlite3_conn),前提是连接字符串启用了共享缓存(cache=shared)——这是实现安全并发读写的必要条件。
? 推荐的连接字符串示例:
db, err := sql.Open("sqlite3", "file:app.db?cache=shared&_journal_mode=WAL&mode=rwc&_busy_timeout=5000")
if err != nil {
log.Fatal(err)
}
// 设置连接池参数以适配高并发场景
db.SetMaxOpenConns(20) // 控制最大并发连接数(非 goroutine 数)
db.SetMaxIdleConns(10) // 保持空闲连接数,减少反复创建开销
db.SetConnMaxLifetime(0) // SQLite 连接无生命周期限制
? 关键参数说明:
- cache=shared:启用进程内共享页缓存,允许多连接安全地读写同一数据库(否则默认 private 模式下并发写必然冲突);
- _journal_mode=WAL:启用 WAL(Write-Ahead Logging)模式,大幅提升读写并发能力(读不阻塞写,写不阻塞读);
- mode=rwc:显式声明读写创建权限,避免因只读打开导致后续写操作失败;
- _busy_timeout=5000:设置忙等待超时为 5 秒(毫秒),使驱动在遇到锁时自动重试,而非立即返回 SQLITE_BUSY 错误。
⚠️ 注意事项:
- ❌ 不要对 *sql.DB 或单个 *sql.Conn 加全局 mutex:这将彻底串行化所有数据库操作,严重损害性能且违背 database/sql 的设计初衷;
- ❌ *避免长时间持有 `sql.Conn(如db.Conn(ctx)后未及时conn.Close())**:会占用连接池资源,导致其他 goroutine 阻塞在GetConn`;
- ✅ 建议统一使用 db.QueryContext() / db.ExecContext() 等上下文感知方法,配合超时控制和取消机制;
- ✅ 对于高频小事务,可考虑批量操作(如 INSERT ... VALUES (...), (...), (...))或使用 BEGIN IMMEDIATE 显式事务减少锁持有时间。
总结:Go 的 mattn/go-sqlite3 驱动已深度集成 SQLite 的并发语义,开发者只需正确配置连接参数、善用连接池、避免连接泄漏,即可在多 goroutine 场景下稳定、高效地使用 SQLite —— 它不是“不能并发”,而是“需要按 SQLite 规则并发”。











