Go 里用 SQLite 最省事的写法
后台服务只有一个进程、一台机器、一两个人在用的时候,我第一反应就是上 SQLite:不用装服务端,不用配账号密码,一个文件拷走就是全部数据。Go 里怎么用 SQLite 最省事,结论是选对驱动加打开正确的几个开关,剩下的事交给它自己。
一、先说适用前提
这套写法适合单机部署、写入不密集的场景。我自己那个后台就是这种:定时任务写日志、手动点几下按钮。
不适合的情况也说清楚:多实例同时读写、写入量持续很高、需要跨机器共享同一份数据。这几种情况硬用 SQLite,后面会一直跟锁打交道。
如果只是「数据量以后会变多」的担心,那不构成换数据库的理由。先跑起来,真到瓶颈再换。
二、第一步:驱动别选纯 Go 实现的那类
Go 生态里有不止一种 SQLite 驱动,差别主要在底层用的是哪套实现。
选的时候看两件事:是否需要在开启某些扩展能力时能用得上,以及是否支持你要的并发方式。纯 Go 的实现编译部署最省事,功能上会有限制。
具体动作:把驱动 import 成空白引用注册,然后在连接串里把参数带上,不要用默认参数直接连。
三、第二步:连接串里先定三个参数
连接串看起来是一串零碎选项,其实只关心三件事。
一是忙等超时,遇到别人正在写的时候先等一下,而不是立刻报错。二是日志模式,决定写入时对其他读的影响方式。三是外键约束,默认可能是关的,表关系不会真的生效。
这三个参数决定了你后面会不会莫名其妙遇到锁的问题,建议写代码的第一天就定下来。
四、第三步:把写入收拢到一个地方
SQLite 同时只允许一个写入者。这件事你改不了,能改的是你的代码怎么配合。
做法是让所有写操作走同一个入口,在应用层排队。读可以放开,写排队。
具体动作:找出代码里所有写数据库的地方,把它们收敛到少数几个函数里,不要让业务代码随手就执行一条写语句。
五、Go 里怎么用 SQLite 最省事:连接池和文件位置
Go 的数据库连接池对 SQLite 不一定越多越好。
写并发被限制住的前提下,开太多连接只会让它们互相等。读多写少的场景,可以给读留几个连接。
具体动作:把最大连接数显式设成一个你解释得清的值,而不是用默认值。设完压一遍,看有没有报锁。
文件位置同样重要。放在网络盘或者同步目录里,锁的行为会变得难以预期。
这类问题排查起来很费时间,因为本地怎么测都没事,一上到那台机器就出问题。数据库文件放本地路径,备份用别的方式做。
六、容易卡住的两个点
第一个是测试环境正常、线上偶发报锁。多半是线上并发写法跟测试不一样,或者文件放的位置不同。
第二个是迁移脚本执行到一半失败,留下一个状态不明的库。执行迁移前先复制一份文件,这个习惯成本很低。
常见问题
Q:Go 里用 SQLite 需要装 CGO 吗?
A:看你选的驱动。有的驱动依赖 CGO,交叉编译会麻烦一些;纯 Go 的不需要。选之前先确认你的部署方式能不能接受。
Q:SQLite 写入慢怎么办?
A:先确认是不是每次写入都在单独提交事务。把批量写收进一个事务里,通常是提升最明显的一步。
Q:一个 Go 服务能用几个 SQLite 文件?
A:技术上可以用多个。但文件一多,事务和数据一致性就要你自己保证,小项目建议先用一个。
相关阅读
- SQLite 和 MySQL 的区别,小项目怎么选
- SQLite 报 database is locked 怎么解决
- SQLite 快撑不住时,会先出现这几条
- SQLite 备份和迁移,两步别搞反
- Go 里 channel 和 mutex 该用哪个
本文作者西米,37 岁程序员,一个人加五个 AI 员工在跑一家公司。踩过的坑和真实账本都写在公众号「西米分享」。
完整代码、踩坑清单与最新进展,都在公众号。
📱 关注公众号「西米分享」