SQLite 一个文件打十个:从搜索、队列到向量库,运维成本砍到零
昨天聊了又上 Hacker News 首页的《PostgreSQL for Everything》,本号的复盘在这里:《PostgreSQL 一个打十个》。有意思的是,joecode.com 的 Joe 看完这波讨论,直接写了一篇《SQLite for Everything》,主张比 Bauer 还要激进:PG 砍掉的是半个中间件栈,SQLite 连数据库服务器都不要,全部塞进一个文件。全文翻译如下,小标题按原文译出,配图为译者所加。
作者:Joe(joecode.com)|2026 年 8 月 19 日
引子
Raphael Bauer 博士写了一篇好文章《PostgreSQL for Everything》,讲用 PostgreSQL 撑起整个企业的好处。我冒昧替他更正了几处错误——主要是他本该选 SQLite 😊(这多半是玩笑,我 ❤️ PostgreSQL,它是好技术。)
流传甚广的说法是,万物终极答案是 42。不是,是 SQLite。(好吧,严格说是 sqlite3。)
SQLite 会活得比你现在正在运行的大部分东西都久。
早些年大家的共识是:SQLite 是个玩具。一个文件。塞进手机 App 里,省得自己写配置解析器的东西。真正的应用要用真正的数据库:有真实的端口号,有真实的守护进程,还有真实的凌晨三点告警。
依我看,SQLite 的力量来自三处:
- 它稳如磐石。
- 它运行、安装、扩容都省事——主要因为压根没什么可运行的东西。
- 它把你的 IT 摊子大幅简化:不光是关系数据库,还是全文搜索引擎、文档库、缓存、向量索引和文件格式。
挨个看。

每个盒子都是一套要部署、要监控、要值班的系统(图源:Pexels)
稳如磐石
SQLite 是那种无聊的老技术,第一版发布于 2000 年。它也是这个星球上部署量最大的数据库引擎,而且不是险胜,是断层领先。它在你手机里、浏览器里、汽车里,在你飞来的那架飞机里。正在运行的 SQLite 副本数量,比其余所有数据库加起来还多,这根本不算一场比赛。
数据库系统里熨平 bug 需要时间,SQLite 有的是时间,而且一直在攒。测试套件在 MC/DC 标准下做到 100% 分支覆盖——那是航空电子软件用的标准。测试代码的体量约是库代码的 500 倍。项目白纸黑字承诺支持到 2050 年,这个规划周期比贵司的使命宣言还长。
它属于公有领域(public domain)。不是开源,是公有领域。没有许可证,没有 CLA,没有署名条款,没有哪家拿了 C 轮融资的厂商哪天回心转意改章程。
SQLite 确实老,但它一直在安静地发新特性:窗口函数、RETURNING、严格表、生成列、jsonb。每次发布都是一次小巧、测透、向后兼容的改进——一个数据库能做的事里,这最不性感,也最值钱。
安装、运行、扩容,全都省事
在本地安装 SQLite 很容易,容易在你早就装完了。每个主流 Linux 发行版都带它;Python、Ruby、PHP、Go、Rust、.NET、Android、iOS 里都内置它;你的 Mac 上现在就坐着一份,不管你有没有点过头。
对「和生产环境一致的数据库」跑测试,在这里不劳 testcontainers 大驾:答案是 :memory:。你的测试套件几微秒就能起一个全新数据库,每个测试一份、可并行、不需要 Docker 守护进程、不会撞端口。你测的东西就是你发的东西,因为是同一个库编译进同一个二进制。
想在服务器上跑 SQLite?你已经在跑了,操作系统自带。
扩容是大家等着这篇文章哑火的部分,那就直说:
- 纵向:当数据库往返是一次函数调用而不是一次网络跳转时,一块现代 NVMe 加一台 128GB 内存的机器能扛住大到离谱的流量。没有连接池,没有 TLS 握手,没有 pgbouncer,纳秒替代毫秒。
- 复制与备份:Litestream 持续把你的 WAL 流式传到 S3,LiteFS 给你分布式读。两者都是小巧的单二进制,无聊而可靠。
- 托管:Turso、Cloudflare D1、rqlite 和它的朋友们,乐意卖你一个带控制面板的 SQLite——如果你想念控制面板的话。
这让 SQLite 成了现存支持最广的软件之一。对你的意义:维护更少,更多时间给客户写功能。
把 IT 摊子变小
在云上跑 SQLite 是零点击:它就是你应用旁边的一个文件。但更好的还在后面——SQLite 能替掉你本来要架起来的一整排系统。
替代 Solr 和 Elastic:全文搜索
SQLite 自带 FTS5,一个内建在你已经链接的库里的全文搜索引擎。分词器、前缀查询、短语查询、NEAR、布尔运算符、BM25 自定义排序,外加渲染结果用的片段和高亮函数。
有两点值得单独说说。第一,没有同步问题,因为压根没有第二个系统。你的搜索索引和你的数据在同一个事务里更新——这是定义使然,永远如此。你经历过的每一次「搜索索引为什么是旧的」事故,根源都是一段你根本不需要的架构。
第二,它快得出人意料。Simon Willison 的 Datasette 对几个 GB 的 SQLite 文件跑分面全文搜索,毫秒级返回,跑在一台小虚机上,不要钱。
FTS5 能做多语言分析链、40 个节点的分布式分片吗?不能。你有 40 个节点吗?也没有。
替代 MongoDB:出色的 JSON 支持
SQLite 存 JSON、查 JSON 的支持相当出色。JSON 函数全部内建,-> 和 ->> 操作符的用法和你想的一样,3.45 版起还有 jsonb——一种二进制表示,省掉每次访问时的重复解析。
被大多数人漏掉的一点:可以往 JSON 内部建索引。从 JSON 路径生成一个生成列,再给这列建索引,你就对 schema 里不存在的字段有了快速查询。写入无 schema,读取有索引,一个文件。
所以话术是:文档存储、ACID 事务、没有独立服务器、没有副本集、没有分片配置、没有 mongod,磁盘上是一个可以直接拷走的单文件。还需要 MongoDB 吗?曾有一家大型出版机构弃用 Mongo 的好文章(译注:指《卫报》2018 年从 MongoDB 迁往 PostgreSQL 的复盘)。值得注意的是,从来没人写过反向的文章。
替代 Kafka 和 RabbitMQ:把 SQLite 当队列
事件、队列、持久化日志,一年比一年重要。Kafka、RabbitMQ、SQS 都提供这些,维护起来烦琐、定制,还得专门雇人。
好消息:一张表就够了。
1 | |
BEGIN IMMEDIATE 提前拿写锁,RETURNING 把抢到的行递给你,事务保证恰好一个 worker 拿到它。WAL 模式下读不阻塞写,看板查队列深度不会和 worker 抢锁。
丑话说在前面,你有资格听:SQLite 只有一个写者。没有 SKIP LOCKED,因为没什么可跳过的。并发消费者在写锁上排队,如果你的入队速率真到每秒几万条,你会感觉到。
但注意这里发生了什么。在 PostgreSQL 版论证里,队列是你数据库里的一张表。到了这一版,队列还是你数据库里的一张表,而这个数据库就在你的应用进程里。消息永远不出这台机器。没有 broker,没有消费组重平衡,没有「为什么部署的时候分区分配变了」。
我的建议:先用 SQLite 当队列。等它真扛不住了,你手里是真实数字而不是一种感觉,可以放心去买 Kafka。你会惊讶这一等有多久。
替代 ClickHouse:高频时序数据
时序数据很特殊:大量数据点快速到达,然后是聚合、统计、汇总。
这里没有 TimescaleDB,那就直说。SQLite 给你的替代方案是:
- 按文件分区。一天、一周或一个租户一个库。归档是
mv,删旧数据是rm——常数时间完成,不用 vacuum,跨库查询是ATTACH加一个UNION ALL视图。粗糙,但极其有效。 - 汇总表由触发器或插入的同一条代码路径写入。你反正也要做持续聚合。
- 批量写入。一个事务、一万条插入、一次 fsync。普通硬件上一秒几十万行,因为路径上没有网络协议。
- 需要列存的时候,把 DuckDB 直接指向你的 SQLite 文件。它原生读取。同一个文件,应用往里写的同时你跑向量化 OLAP,不用 ETL。
专用系统确实厉害,如果你每秒要吞一百万个点,请去用它们。但大多数说「时序」的人,指的是一天几百万行——对 SSD 上的一个文件来说,这只是个平淡无奇的星期二。
给 AI 工作流当向量数据库
sqlite-vec 是一个单文件、零依赖的扩展,把 SQLite 变成向量数据库。C 写的,SQLite 能跑哪它就能跑哪,包括通过 WASM 跑在浏览器里,向量就存在普通表里。
这是 SQLite 占尽便宜的地方。你的 embedding、原始文档、元数据和全文索引在同一个文件里,所以混合检索是一个 JOIN,而不是横跨三个服务、三种一致性模型的分布式查询。按租户、日期、关键词加向量相似度过滤,一条语句,事务性完成。
还有一点比听起来更重要:你整个 RAG 索引就是一个文件。可以拿邮件发出去,可以塞进 Docker 镜像,可以发给一台离线的笔记本。拿你的托管向量集群试试这个。
替代 Redis:不追求持久化的高性能缓存
缓存很重要,多数应用拿 Redis 存会话和热点数据。缓存的定义就允许丢数据、允许从源头重建。
那为什么为这个单跑一台服务器?SQLite 按你愿意交出多少持久性,给你几档选择:
1 | |
或者干脆不落盘::memory:、PRAGMA temp_store = MEMORY,或者用 file:cache?mode=memory&cache=shared 让多个连接共享一个内存库。
过期就是一个字段加一个定时跑的 DELETE ... WHERE expires_at < unixepoch()——Redis 替你干的本来也是这个,只是离得更远,还附送一套你得自己去读的驱逐策略。
关键数字来了:Redis 走本机 localhost 的 GET 大约 100 微秒量级,SQLite 对着热页缓存做点查询大约 1 微秒。你不是减掉一个依赖然后变慢了,你是减掉一个依赖然后变快了,因为最快的网络调用,是不用走网络、直接函数调用的那次。
Redis 是优秀软件。但它也是一个独立进程、一种独立故障模式、一份独立内存预算、一个要单独加固的东西、账单上单独的一行。
替代文件系统:存原始数据
你会以为从文件里读一个小 blob 比从数据库里读快。并不是,而且这不是观点,是 SQLite 项目发过的基准测试,标题起得直白得可敬:《35% Faster Than the Filesystem》(比文件系统快 35%)。
大约 100KB 以下的 blob,SQLite 的读写都快过磁盘上的独立文件,空间还省 20% 左右。原因:文件系统每个条目收你一次 open()、一次 close() 外加一次目录遍历,SQLite 只收一个已打开的文件句柄加一次 B 树查找。
白送的还有:原子性地一次更新多个 blob、崩溃时没有半截写入、没有文件名转义 bug、没有「目录里 400 万个条目怎么办」、没有 rsync 因为 inode 数量跑六个小时,备份方案就是一个文件。
载荷存 BLOB 列,讲究点的用紧凑格式序列化,客户端反序列化。SQLite 团队自己说过,SQLite 是一个更好的 fopen()——这是设计目标,不是玩笑。
替代图数据库
层级数据在 SQL 里用递归查询做得到,但历来难读、难维护、难调试。
SQLite 有完整的递归 CTE 支持,官方文档还是这个领域数得着的好技术写作。闭包表、物化路径、邻接表都好用。没有 LTREE,物化路径就用 TEXT 列加 GLOB 索引——丑一点,速度差不多。
真要跑图,simple-graph 用几百行 SQL 在普通 SQLite 表上实现了一个属性图,节点、边、遍历,齐了。
那条通用原则在这里比哪里都适用:你的图大概就一万个节点。一万个节点塞得进 L3 缓存。你不需要 Neo4j,你需要的是一个索引和一杯咖啡。
替代微服务
今天大多数「微服务」干的事是:一个模型、一条查询、吐 JSON。
SQLite 用 json_object() 和 json_group_array() 把任意查询变成 JSON。你的序列化层,没了。
但 SQLite 比原论证走得更远,因为它跑在你的进程里。微服务不是被存储过程替代,是被一次函数调用替代。没有服务要部署,没有健康检查,没有重试逻辑,没有熔断器,没有要关联的分布式链路追踪,p99 里也没有网络抖动的份。
Datasette 是这套思路走到头的样板:指向一个 SQLite 文件,你就得到 JSON API、Web 界面、分面搜索和插件生态,零代码。持久化交给 Litestream。两个二进制加一个文件,一套生产级数据服务。
有得有失,我不会假装代价是零。但这个行业里,纯为给一条查询前面垫一次网络跳转而存在的服务,数量不少。
替代 PlayStation 5

数独求解器、国际象棋引擎、Doom 火焰特效,都是拿 SQL 写的(图源:Pexels)
SQLite 官方文档里有一个用递归 CTE 写的曼德博集合渲染器。就在手册里。作为查询语法的示例。若无其事。
大家还用纯 SQLite CTE 实现过康威生命游戏、数独求解器、迷宫生成器。有国际象棋引擎,有人把 Doom 的火焰特效跑在了一条查询里。
疯的,大概不必当真。但你得尊重一个官方文档里印着分形的数据库。
结语
上面这份清单不全。SQLite 是一块弹性惊人的软件,它可以加载扩展,你想去装服务器的那个用途,八成已经有人写好扩展了。
原论证说对的、SQLite 说得更对的一点是:简单才能快。你技术栈里的每个系统,都是一件要部署、监控、加固、升级、备份、付钱、还要向新同事解释的东西。PostgreSQL 把这份清单剪短,SQLite 把它剪到零,因为数据库不再是「一个系统」,它是一个文件和一次函数调用。
是的,有天花板:一个写者,一台机器。撞到那天你会知道的,然后你会去请 PostgreSQL,那是好日子——因为那说明有人在用你做的东西。
在那之前,当下一个需求出现时,问一句:SQLite 是不是也能干?我们真的需要那个闪亮的新技术 X 吗?
SQLite 未必能回答一切,但它能回答的比你以为的多得多——而且它已经装好了。
两篇对照着读最有意思:《PostgreSQL 一个打十个》讲 PG 能替掉哪些中间件、边界在哪;这一篇讲 SQLite 连 PG 那台服务器都想去掉。边界问题我还是昨天的看法:专用系统应该被真实的瓶颈逼出来,别在第一天就写进架构图。