
使用 Go 操作 SQLite 时,应为每个 goroutine 分配独立的 database/sql 连接(而非共享单个连接),并启用 cache=shared 和 mode=rwc 参数以支持安全的并发读写;驱动本身会自动处理底层锁重试逻辑,无需手动加锁。
使用 go 操作 sqlite 时,应为每个 goroutine 分配独立的 database/sql 连接(而非共享单个连接),并启用 `cache=shared` 和 `mode=rwc` 参数以支持安全的并发读写;驱动本身会自动处理底层锁重试逻辑,无需手动加锁。
在 Go 中通过 database/sql + github.com/mattn/go-sqlite3 访问 SQLite 数据库时,并发安全的关键不在于手动加锁(如 sync.Mutex 或 channel 序列化),而在于合理管理连接生命周期与连接参数配置。
SQLite 本身是文件级数据库,其并发模型基于“写序列化”:任意时刻仅允许一个写操作进行,读操作可并发,但写操作会独占数据库文件(触发 RESERVED → PENDING → EXCLUSIVE 状态跃迁)。mattn/go-sqlite3 驱动在底层封装了 SQLite 的 busy handler 机制,默认会在遇到 SQLITE_BUSY 错误时自动重试(默认重试 1 秒,可通过 busy_timeout 参数调整),因此你不需要捕获锁错误后手动重试——只要连接配置得当,驱动会静默处理临时阻塞。
✅ 正确做法:
- 使用 sql.Open() 创建连接池(非单例连接),让 database/sql 自动复用和管理连接;
- *每个 goroutine 应调用 db.Query(), db.Exec() 等方法获取独立的 `sql.Conn或直接使用连接池上下文**,避免跨 goroutine 复用同一sql.Tx或裸sqlite3.Conn`;
- 在 DSN 中启用共享缓存与读写创建模式:
dsn := "file:app.db?cache=shared&mode=rwc&_busy_timeout=5000"
db, err := sql.Open("sqlite3", dsn)
if err != nil {
log.Fatal(err)
}
db.SetMaxOpenConns(20) // 根据负载调整连接池大小
db.SetMaxIdleConns(10)⚠️ 关键参数说明:
- cache=shared:启用进程内共享页缓存,允许多连接协同访问同一数据库文件,显著减少锁冲突(默认为 private,各连接隔离缓存,易引发不必要的写等待);
- mode=rwc:确保数据库以读写创建模式打开(等价于 ?_journal_mode=WAL 可选,但 WAL 模式需额外启用);
- _busy_timeout=5000:将默认忙等待超时从 1000ms 提升至 5000ms,给写操作更充分的抢占窗口(单位:毫秒);
- 若追求更高并发吞吐,建议显式启用 WAL 模式(Write-Ahead Logging):在首次初始化时执行 PRAGMA journal_mode = WAL,它允许多读+单写并发,大幅降低读写互斥。
❌ 常见误区:
- 用全局 *sql.DB 实例配合 sync.Mutex 手动串行化所有 SQL 调用 → 完全违背连接池设计初衷,严重损害性能;
- 复用 *sql.Tx 跨 goroutine → 导致未定义行为甚至 panic;
- 忽略 SetMaxOpenConns,任由高并发创建海量连接 → 触发 SQLite 文件句柄耗尽或系统级资源瓶颈。
? 总结:SQLite 在 Go 中的并发安全 = 合理连接池配置 + 共享缓存 + WAL 模式(推荐)+ 驱动内置 busy handler。你只需信任 database/sql 的抽象层,专注业务逻辑,把锁管理交给驱动和 SQLite 内核。

















