2026-08-16
· Dylan YuSQLite vs PostgreSQL:如何为下一个项目选择合适的数据库
SQLite 和 PostgreSQL 解决的是不同的问题,但网上大多数建议都把其中一个说成普遍更好。这里有一套实用的决策框架,基于你实际在构建什么——并发、规模、部署,以及什么时候你可能两个都需要。
有个问题我见过无数次的糟糕回答:"我该用 SQLite 还是 PostgreSQL?"
典型的回答差不多就是"SQLite 用来做原型,PostgreSQL 用来上生产"。这不仅是过度简化——它还会误导人。它让人为了一个单用户的 CLI 工具去搭一个 Postgres 服务器(大材小用),或者把 SQLite 作为多写入 SaaS 的主数据库(迟早出事)。
诚实的答案是,它们各自为根本不同的场景做了优化。正确的选择取决于你工作负载的具体特征,而大多数对比文章从来不问这些。我们来逐一过一遍。
核心区别(大多数对比文章都跳过的)
SQLite 是一个库。你把它链接到你的应用进程里,它读写磁盘上的一个文件。没有服务器,没有网络,没有认证握手。你的应用和数据库在同一个进程里。
PostgreSQL 是一个服务器。它作为独立的守护进程运行,管理自己的内存和连接池,通过网络协议接收查询。你的应用作为客户端连接到它。
这一个架构差异几乎解释了人们争论的所有其他差异:
- 并发 — Postgres 用 MVCC 处理数百个同时连接。SQLite 通过一个全局锁来串行化写入(即使在 WAL 模式下)。
- 部署 — SQLite 就是
npm install better-sqlite3,搞定。Postgres 是一个你需要运行、监控、备份并保持存活的服务器。 - 规模 — Postgres 为跨多块磁盘的 TB 级数据而设计。SQLite 为单机上的 GB 级数据而设计。
- 延迟 — SQLite 查询是微秒级的,因为没有网络往返。Postgres 查询是毫秒级的,因为总有一跳网络(即使在 localhost 上)。
这些都不意味着哪个"更好"。它们让其中一个对特定工作负载更合适。
什么时候该用 SQLite
1. 单进程应用
如果你的应用作为单个进程运行——CLI 工具、桌面应用、移动应用、单服务器 webhook 处理器——SQLite 几乎总是正确的默认选择。当只有一个客户端时,单独的数据库服务器没有任何好处。
我会毫不犹豫地选择 SQLite 的场景:
- 需要本地存储的桌面应用(BaseVolt 自己就用 SQLite 存配置)
- 在多次运行之间跟踪状态的 CLI 工具
- 移动应用(SQLite 已经内嵌在 iOS 和 Android 里)
- 你还不知道 schema 的原型或 MVP
- 由预计算的
.db文件支撑的读多分析型 dashboard
2. 嵌入式和边缘场景
SQLite 的"无服务器"特性在边缘端变成了超能力。Cloudflare D1 就是跑在 Cloudflare 边缘网络上的 SQLite。Turso 和 libSQL 是为分布式读取优化的 SQLite 分支。如果你在构建靠近用户运行的东西——CDN 边缘上的 worker、IoT 设备、嵌入式系统——SQLite 是唯一合理的选择。Postgres 没法以一种你想维护的方式跑在 Raspberry Pi Zero 上。
3. 读多、低写入竞争的工作负载
有个让人意外的事实:WAL 模式下的 SQLite 可以处理数千个并发读取者,没有竞争。读取者不阻塞写入者,写入者也不阻塞读取者。限制只在并发写入——一次只能有一个写入者。
如果你的工作负载 99% 是读取(CMS、产品目录、配置存储、基于预计算数据的分析 dashboard),SQLite 能比你想象的扩展得更远。"SQLite 不能扩展"这个说法主要是针对写多的多用户应用,不是读多的。
4. 当你不想运维数据库的时候
这一点被低估了。在生产环境跑 Postgres 意味着:
- 设置连接池(PgBouncer 或内置池化器)
- 配置
shared_buffers、work_mem、max_connections、wal_level - 设置自动备份并测试恢复
- 监控复制延迟、vacuum 膨胀、锁竞争
- 用
pg_upgrade或逻辑复制升级大版本
如果你是独立开发者或没有专职 DBA 的小团队,以上每一项都是凌晨三点可能出问题的地方。SQLite 一个都没有。备份就是 cp database.db database.db.bak。
什么时候该用 PostgreSQL
1. 多进程或多服务器工作负载
当你有两台应用服务器需要读写同一份数据的那一刻,SQLite 就出局了。你可以把 SQLite 文件放在网络共享上,但你不应该这么做——NFS/SMB 上的文件锁是个定时炸弹,迟早会损坏你的数据库。
Postgres 正是为这种场景设计的。多台应用服务器连接到一个 Postgres 实例,Postgres 正确处理并发。如果你运行多个应用副本,你需要一个服务器端数据库。
2. 高写入并发工作负载
如果你有数十个并发写入者——有活跃用户的 SaaS、日志管道、实时分析入库——SQLite 的单写入者锁就成了瓶颈。即使在 WAL 模式下,写入者也要排队。Postgres 用 MVCC 处理这个:多个写入者可以同时修改不同的行,冲突在提交时解决。
经验法则:如果"来自不同进程的并发写入"是你工作负载的核心特征,你需要 Postgres(或 MySQL,或其他服务器端 RDBMS)。
3. 你真正需要的高级 SQL 功能
Postgres 有 SQLite 没有的功能,其中一些很重要:
- 窗口函数 — SQLite 现在也有了,但 Postgres 的实现更完整
- 物化视图 — Postgres 原生支持;SQLite 没有
- 全文搜索 — 两者都有 FTS,但 Postgres 的
tsvector/tsquery更强大 - JSON/JSONB — 两者都支持 JSON,但 Postgres 的 JSONB 配合 GIN 索引完全不在一个级别
- PostGIS — 如果你需要地理空间查询,PostGIS 是黄金标准
- 逻辑复制、分区、LISTEN/NOTIFY — 服务器端功能,SQLite 没有对应物
上面关键词是*"你真正需要"*。大多数应用不用这些功能的大部分。但如果你在构建地理空间应用,或者需要通过 LISTEN/NOTIFY 做实时推送,或者在大规模查询嵌套 JSON,Postgres 就是答案。
4. 数据库层面的严格数据完整性
SQLite 以默认宽松著称。它使用动态类型——你可以往整数列里插入字符串,SQLite 会愉快地存下来。外键约束默认关闭(你必须在每个连接上 PRAGMA foreign_keys = ON)。Postgres 默认严格:类型不匹配会报错,外键被强制执行,约束不可商量。
如果你在团队中工作,希望数据库层面强制完整性而不是依赖应用代码,Postgres 的严格是功能,不是 bug。
对比表(附带诚实的权衡)
| 维度 | SQLite | PostgreSQL |
|---|---|---|
| 架构 | 嵌入式库 | 客户端-服务器 |
| 安装时间 | 秒级(一个文件) | 分钟到小时(服务器配置) |
| 并发读取者 | 数千(WAL 模式) | 数千 |
| 并发写入者 | 1(串行化) | 数百(MVCC) |
| 实际最大容量 | 单文件约 1TB | TB+ |
| 网络延迟 | 无(进程内) | 即使 localhost 也有 0.1-2ms |
| 备份 | 复制文件 | pg_dump / WAL 归档 / 快照 |
| 运维负担 | 几乎为零 | 不小 |
| 成本 | 免费 | 免费(自托管)或 $$(托管) |
| 严格类型 | 可选(默认动态) | 强制执行 |
| JSON 支持 | JSON1 扩展 | JSONB 配 GIN 索引 |
| 全文搜索 | FTS5 | tsvector 带排序 |
| 地理空间 | 有限 | PostGIS(同类最佳) |
| 复制 | 非内置(litestream 等) | 内置流式 + 逻辑复制 |
| 最适合 | 单进程、边缘、读多 | 多服务器、写多、复杂 |
实用决策框架
与其给你一个你会跳过的流程图,不如看这四个真正决定答案的问题。
1. 有多少进程会同时写入这个数据库?
- 一个 → SQLite 没问题
- 多于一个 → Postgres
2. 数据库会分布在多台机器上吗?
- 不会,单机 → SQLite 没问题
- 会,多服务器/副本 → Postgres
3. 你需要 Postgres 独有的高级功能吗(PostGIS、物化视图、大规模 JSONB、逻辑复制)?
- 不需要 → SQLite 没问题
- 需要 → Postgres
4. 你的团队运维数据库服务器的能力是约束条件吗?
- 是,我们没有 DBA 也没时间 → SQLite(或托管 Postgres)
- 不是,我们能跑 Postgres → Postgres
如果你对三个或更多问题回答了"SQLite 没问题",用 SQLite。如果你对两个或更多回答了"Postgres",用 Postgres。边缘情况比网上说的要少得多。
现实:很多团队两个都用
没人告诉你的是:选择不总是非此即彼。
我在运行良好的团队中见过一个常见的生产模式:
- PostgreSQL 作为主操作数据库(多服务器、高写入、事实来源)
- SQLite 作为本地缓存、边缘副本或单机上的分析快照
Cloudflare D1(边缘 SQLite)配合 Postgres 主库是全球分布式应用的合理架构。Turso + Postgres 是同样的思路。你写入 Postgres,复制到靠近用户的 SQLite 实例,以零延迟在本地读取。
这种设置的摩擦不在数据库——而在工具。大多数管理面板只为其中一种而建。你最终用 DBeaver 连 Postgres,用 DB Browser 连 SQLite,每种的工作流完全不同。
这也是我们构建 BaseVolt 同时支持两者的原因之一。指向一个 .db 文件用于 SQLite,或给它一个 Postgres 连接字符串,你就得到相同的管理界面——网格视图、kanban 看板、dashboard、关系配置——无论连接的是哪个数据库。对于同时使用两者的团队,这意味着一个工具而不是两个。(如果你想先看看再安装,demo.basevolt.app 有在线演示。)
我见过的常见错误
因为"更简单"而用 SQLite 做多写入 SaaS。 它确实更简单——直到你的第二个用户和第一个用户同时写入,你碰到了 SQLITE_BUSY。我在生产环境见过这个。用 Postgres。
为单用户 CLI 工具搭 Postgres。 你现在有了一个需要备份、监控和升级的数据库服务器,服务的是一个一个人用的应用。用 SQLite。
因为"不能扩展"而否定 SQLite。 它能扩展到数百 GB 和数千并发读取者。它不能做的是多进程并发写入。如果那不是你的工作负载,SQLite 扩展得比你想象的远。
把 Postgres 默认配置当生产就绪。 不是。max_connections = 100 不带连接池在任何真实负载下都会出问题。如果你跑 Postgres,了解 PgBouncer、shared_buffers 和 wal_compression——或者用托管服务(Neon、Supabase、RDS)帮你处理这些。
总结一下
SQLite 和 PostgreSQL 不是竞争者。它们是做不同工作的工具。
- SQLite 用于数据库和应用在一起:桌面应用、移动应用、边缘函数、CLI 工具、读多的单服务器工作负载、原型。
- PostgreSQL 用于数据库服务多个客户端:多服务器 web 应用、高写入并发工作负载、需要高级 SQL 功能的应用、数据库层面严格完整性很重要的任何场景。
错误的做法是根据哪个"更强大"来选。根据你的工作负载实际做什么来选。大多数应用不需要数据库服务器。有些应用没有就不行。知道哪个是哪个才是真正的技能。
如果你在用其中之一(或两者),BaseVolt 给你一个本地优先的 SQLite 和 PostgreSQL 管理面板,不把你的数据发到任何地方。 无需账号,无需云端,离线可用。
在用 SQLite 或 Postgres 做东西?我很好奇你在做什么——在 X 上找我。