唯一靠谱路径是用 database/sql + mattn/go-sqlite3:需启用 CGO,空导入驱动,db.Ping() 验证连接,手动执行 PRAGMA 启用 WAL、外键和 UTF-8,并注意事务内锁序避免死锁。

用 database/sql + mattn/go-sqlite3 是唯一靠谱路径
Go 官方没有内置 SQLite 驱动,database/sql 只是接口层,必须配第三方驱动。目前稳定、维护活跃、支持 CGO(必要)的只有 mattn/go-sqlite3。别试 go-sqlite 或纯 Go 实现的替代品——要么不支持 WAL、绑定参数异常,要么连 PRAGMA journal_mode 都设不了。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 必须启用 CGO:
CGO_ENABLED=1,否则编译直接报错undefined: sqlite3.SQLiteDriver - 安装命令不是
go get sqlite3,而是:go get -u github.com/mattn/go-sqlite3 - import 时不能只写
"github.com/mattn/go-sqlite3",必须确保它被“触达”:要么在 main 包 import 空导入(_ "github.com/mattn/go-sqlite3"),要么显式调用sql.Register
sql.Open 不真连数据库,db.Ping() 才是第一道验证
很多人写完 sql.Open("sqlite3", "test.db") 就以为连上了,结果后续 Exec 报 no such table 或 disk I/O error 才发现文件路径错了、目录没写权限、甚至 SQLite 被编译成 disabled 状态。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
-
sql.Open只做驱动注册和连接池初始化,返回的*sql.DB即使传入错误 DSN 也不会 panic - 务必紧接着调
db.Ping(),它会真正打开文件并尝试一次轻量交互;失败时返回具体错误,比如no such file or directory(路径不存在)、permission denied(目录不可写) - SQLite 的 DSN 实际只是文件路径,不支持 query 参数(如
?cache=shared),但可以加?_pragma=journal_mode=WAL这类特殊语法(见下条)
WAL 模式、外键、UTF-8 要靠 PRAGMA 显式开启
SQLite 默认关闭外键约束、用 DELETE 日志模式、不校验字符编码。Go 驱动不会帮你自动执行这些 PRAGMA,全靠你自己在建表前或 Open 后手动执行。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 开启 WAL:
_, _ = db.Exec("PRAGMA journal_mode = WAL");不设这个,高并发读写容易卡住 - 启用外键:
_, _ = db.Exec("PRAGMA foreign_keys = ON");否则FOREIGN KEY语句形同虚设 - 强制 UTF-8:
_, _ = db.Exec("PRAGMA encoding = 'UTF-8'");避免中文插入变???(尤其 Windows 下默认 ANSI 编码) - 这些
PRAGMA必须在任何建表或操作前执行,且每个新连接都要重设(除非用连接池 +db.SetConnMaxLifetime配合Init函数)
事务里别混用 QueryRow 和 Exec,小心死锁
SQLite 是单写多读的文件锁模型。在事务中如果先用 QueryRow 查数据(触发 SELECT 锁),再用 Exec 改数据(需要 RESERVED 锁),而另一个 goroutine 反过来操作,就可能因锁序不一致导致死锁 —— 表现为 database is locked 且超时后才返回。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 事务内尽量统一访问模式:读多写少用
Query/QueryRow,写多读少优先Exec再查 - 避免在事务里调用未预编译的语句;用
stmt, _ := db.Prepare(...)提前编译,减少锁持有时间 - 设置合理超时:
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second),传给db.BeginTx(ctx, nil),别让一个卡住的事务拖垮整个连接池
SQLite 在 Go 里不是“能跑就行”的玩具,它的锁行为、WAL 切换时机、PRAGMA 生效范围都和你直觉有偏差。最常漏掉的是 Ping() 验证和事务内锁序控制——这两处一错,问题往往延迟暴露,排查成本远高于初始化时多写三行代码。


















