sql.open("sqlite3", ...) 连不上或报错“no such file or directory”主因是驱动未注册,需显式导入 _ "github.com/mattn/go-sqlite3";dsn路径基于工作目录,建议用绝对路径;sqlite不自动创建父目录,须确保路径存在且有写权限。

sql.Open("sqlite3", ...) 为什么连不上或报错 no such file or directory
不是路径写错了,而是驱动没注册。Go 的 database/sql 是接口层,必须显式导入并初始化驱动。只写 import "database/sql" 不会自动加载 SQLite 支持。
正确做法是:用下划线导入驱动包,并确保它在 main 包中被引用
- 执行
go get github.com/mattn/go-sqlite3(注意:这是基于 Cgo 的主流驱动,非纯 Go 的go-sqlite) - 在代码里加
_ "github.com/mattn/go-sqlite3",不能省略下划线 - DSN 中的路径是相对当前工作目录(
os.Getwd()),不是相对于源文件;建议用绝对路径或filepath.Join(os.Getenv("PWD"), "data.db")显式控制 - 若报
no such file or directory,先确认目录存在且进程有写权限;SQLite 不会自动创建父目录
INSERT 时用 Exec 还是 Prepare?什么时候必须用事务
单条插入用 Exec 简单直接;批量插入(>10 行)或含用户输入时,必须用 Prepare + Exec 防 SQL 注入。但真正需要事务的场景不是“多条语句”,而是“逻辑上不可分割的一组操作”。
-
Exec("INSERT INTO users(name) VALUES(?)", name)安全,但每次调用都触发一次编译和参数绑定 - 高频插入建议复用
*sql.Stmt:一次Prepare,多次Exec,性能提升明显 - 事务不是用来“加速”的,而是保 ACID:比如转账要同时扣 A 账户、加 B 账户,任一失败必须全部回滚;用
tx, _ := db.Begin()启动,显式tx.Commit()或tx.Rollback() - SQLite 默认每条语句自动提交;开启事务后,所有语句都在同一锁级别下执行,避免并发写冲突
为什么 SELECT 查询返回空结果,但数据库里明明有数据
最常见原因是列名大小写不一致或类型隐式转换失败。SQLite 是动态类型系统,但 Go 的 Scan 对类型很敏感,且不自动做列别名映射。
- 别写
SELECT * FROM users然后用结构体 Scan —— 列顺序、数量、类型必须完全匹配;优先用明确字段名:SELECT id, name, email FROM users - 如果某列可能为 NULL(如
email TEXT允许为空),Go 中必须用指针或sql.NullString接收,否则 Scan 会报sql: Scan error on column index 2 - 时间字段用
CURRENT_TIMESTAMP写入的是字符串格式(如"2026-07-14 16:40:00"),Go 中需用time.Time接收,但要确保数据库连接 DSN 加上_loc=Local参数,否则时区解析可能出错:"./app.db?_loc=Local" - 表名或字段名含特殊字符(如中划线)必须用双引号包裹:
SELECT "user-id" FROM "user-table"
嵌入式场景下如何安全关闭数据库和清理资源
SQLite 文件即数据库,没有服务进程,但 Go 的 *sql.DB 是连接池抽象,关不关影响不大;真正关键的是确保写操作已落盘、无活跃语句、无未提交事务。
-
db.Close()只是关闭连接池,不强制刷盘;SQLite 默认启用 WAL 模式时,关库前最好执行PRAGMA wal_checkpoint(TRUNCATE)强制合并日志 - 不要在 defer 中无条件
db.Close()—— 如果db初始化失败,db是 nil,调用 Close 会 panic - 长时间运行的嵌入式应用(如桌面工具),可忽略主动 Close;但 CLI 工具或测试用例务必 Close,否则可能残留
-wal和-shm文件 - SQLite 不支持并发写;多 goroutine 写入必须串行化(用 mutex 或 channel 控制),否则容易遇到
database is locked
实际部署时最容易被忽略的点是:SQLite 的并发模型和错误重试机制。它不像 PostgreSQL 那样自动重试死锁,database is locked 出现时,你需要自己捕获该错误并 sleep 后重试 —— 这在嵌入式或桌面应用中几乎总是必要的。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











