唯一可行路径是database/sql+mattn/go-sqlite3:必须启用CGO、空导入驱动、db.Ping()验证连接,手动PRAGMA启用WAL/外键/UTF-8,并在事务中统一使用*sql.Tx操作。

用 database/sql + mattn/go-sqlite3 是唯一稳定可行的路径,别试纯 Go 实现的驱动——它们连 PRAGMA journal_mode = WAL 都设不了,高并发下大概率卡死。
必须启用 CGO 且正确导入驱动
不启用 CGO 会直接报 undefined: sqlite3.SQLiteDriver;只写 import "github.com/mattn/go-sqlite3" 不生效,必须空导入:import _ "github.com/mattn/go-sqlite3"。安装命令是 go get -u github.com/mattn/go-sqlite3,不是 go get sqlite3。
常见错误现象:
- Alpine 容器里构建失败:缺
musl-dev和sqlite-dev,要先apk add --no-cache musl-dev sqlite-dev - macOS 上编译报找不到头文件:检查
PKG_CONFIG_PATH是否指向 Homebrew 的lib/pkgconfig - Windows 下用 MinGW:确保
gcc在 PATH,且版本 ≥ 9.0
sql.Open 不等于连接成功,db.Ping() 才是第一道验证
sql.Open 只初始化连接池和驱动,传入 "sqlite3" 和任意乱写的路径(比如 "xxx.db")也不会 panic。真正出问题在第一次 I/O —— 所以务必紧跟 db.Ping()。
立即学习“go语言免费学习笔记(深入)”;
典型错误反馈:
-
no such file or directory:路径不存在,或上层目录没写权限 -
permission denied:目录不可写,尤其在 Docker 挂载只读卷时 -
disk I/O error:SQLite 被编译为 disabled 状态(极少见,但交叉编译时可能)
DSN 就是文件路径,不支持标准 URL 参数(如 ?cache=shared),但可带 _pragma 形式参数:"my.db?_busy_timeout=5000&_journal_mode=WAL",注意这是 SQLite 驱动解析的特殊语法,不是 net/url。
WAL、外键、UTF-8 必须手动 PRAGMA 开启
SQLite 默认关闭外键、用 DELETE 日志模式、不校验编码。Go 驱动不会帮你自动执行这些,全靠你建表前显式调用 db.Exec:
-
_, _ = db.Exec("PRAGMA journal_mode = WAL"):否则多 goroutine 写入会频繁阻塞 -
_, _ = db.Exec("PRAGMA foreign_keys = ON"):否则FOREIGN KEY约束完全无效 -
_, _ = db.Exec("PRAGMA encoding = 'UTF-8'"):Windows 下默认 ANSI,中文插入变???
这些 PRAGMA 对每个新连接都需重设。如果用连接池,得配合 db.SetConnMaxLifetime 和自定义 Init 函数,否则旧连接可能失效。
事务中所有操作必须走 *sql.Tx,混用 db.Query 就破功
SQLite 是文件锁模型,db.Begin() 返回的 *sql.Tx 才持有锁。一旦在事务里调 db.Query 或 db.Exec,就脱离事务上下文:
- 该语句不参与回滚
- 不被写锁保护,可能读到中间态
- 更糟的是:某些驱动下
db.Exec在事务中会触发隐式提交
正确做法是统一用 tx.Query / tx.Exec,并始终 defer tx.Rollback(),成功后显式 tx.Commit()。别依赖 tx, _ := db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelSerializable}) —— SQLite 不支持串行化隔离级别,设了也白设。


















