必须显式导入 _ "github.com/mattn/go-sqlite3" 以注册驱动,仅 go get 安装不生效;windows 编译失败时可换纯 go 驱动 github.com/glebarez/sqlite。

GoLand 里配置 SQLite 数据库驱动失败,报 driver: unknown driver "sqlite3" (forgotten import?)
这是最常卡住的第一步:Go 代码里写了 sql.Open("sqlite3", "..."),但编译直接报错。根本原因不是驱动没装,而是没在代码里显式导入——Go 的 database/sql 是接口层,驱动必须被主动“唤醒”。
解决方式只有一条路:_ "github.com/mattn/go-sqlite3" 这行必须出现在 main.go 或初始化文件的 imports 中,且不能删掉下划线(_ 表示只执行包的 init() 函数,不引入符号)。漏掉这行,驱动就等于不存在。
- 别用
go get github.com/mattn/go-sqlite3后就以为完事了;它只是下载,没注册驱动 - Windows 下如果报编译错误(如
gcc: not found),说明缺少 C 工具链;直接换预编译版驱动:go get -u github.com/glebarez/sqlite(纯 Go 实现,无需 CGO) - GoLand 的自动 import 功能不会帮你补这行,得手敲
消息队列用 SQLite 做持久化,建表语句里该不该加 PRIMARY KEY AUTOINCREMENT
应该加,但仅限于主键是整数且你明确需要单调递增 ID 的场景。对消息队列来说,AUTOINCREMENT 不是必需的,反而可能带来两个隐藏问题:
- SQLite 的
AUTOINCREMENT会维护一个内部计数器(sqlite_sequence表),每次插入都需额外写入和同步,吞吐下降约 10%~15% - 如果消息体本身带时间戳或 UUID,用它们做主键更利于按时间范围查询,而
AUTOINCREMENTID 和消费顺序无关,容易误导逻辑 - 真正需要的是唯一性 + 高效索引,所以推荐:主键用
id TEXT PRIMARY KEY(存 UUID),再单独建CREATE INDEX idx_created_at ON messages(created_at)
从 GoLand 调试窗口看 db.Exec() 返回 sql.ErrNoRows,但明明刚插入了一条消息
这不是数据没写进去,而是你误用了查询类 API 去执行写操作。典型错误写法:_, err := db.Query("INSERT INTO ...") 或 db.QueryRow("INSERT ...")。
GoLand 2026.1.1 是 2026.1 发布后的首个维护修正版本,适合已经开始体验 2026.1 新功能并希望同步补丁的开发者。它更适合用于入门项目、现有项目迁移测试和 IDE 行为验证。
SQLite 的 INSERT/UPDATE/DELETE 必须用 db.Exec(),它返回的是 sql.Result(含影响行数),不是结果集。一旦你用 Query 系列方法执行写语句,SQLite 驱动会尝试构造空结果集,然后返回 sql.ErrNoRows —— 这个错误信息极具迷惑性,但它和“没查到数据”完全无关。
- 所有写操作(INSERT/UPDATE/DELETE)一律用
db.Exec() - 所有读操作(SELECT)才用
db.Query()或db.QueryRow() - GoLand 调试时看到
err != nil就停住,先看 err 的具体类型和 message,别条件反射去查数据库
并发写入消息时,SQLite 报 database is locked
SQLite 默认使用 DEFERRED 事务,写操作一多就容易锁表。这不是 Go 代码写错了,是没调好连接行为。关键点有三个:
- 在
sql.Open()后立刻设置连接池:db.SetMaxOpenConns(1)(单写场景下最稳)或db.SetMaxOpenConns(5)(读多写少可放宽) - 打开连接时追加
_busy_timeout=5000参数,比如:"file:msg.db?_busy_timeout=5000",让冲突时等待而非立即报错 - 避免在事务外执行长耗时操作(如网络请求、日志打印),否则锁持有时间被拉长,放大冲突概率
SQLite 本身不支持真正的并行写,所谓“高并发”在它身上本质是快速排队。想撑更高写入量,得换 RocksDB 或轻量级服务型存储,而不是硬调参数。










