SQLite 是世界上部署最广的数据库——每台 iOS/Android 手机里有几百个 SQLite 库,每个浏览器、每个 macOS/Windows 系统组件都在用,连 Notion / ChatGPT / Slack 桌面端都把它当本地缓存。但很多后端开发者还在把它当成”小项目玩具”,遇到真实业务就一律上 Postgres——这其实是浪费。
这篇拆解 SQLite 的真实生产能力、并发瓶颈出现在哪、什么场景该选 SQLite 而不是 Postgres / MySQL。
SQLite 的真实定位
SQLite 官方文档里有一篇 “When To Use SQLite”,明确说它的目标不是和 Postgres 竞争,而是”取代 fopen()“——做嵌入式、单机、本地存储。
适合场景:
- 嵌入式应用:桌面 / 移动 / IoT / 浏览器扩展
- 单机 Web 服务 / CLI 工具:个人博客、文档站、内部工具
- 读多写少的 SaaS:单租户、SaaS 阅读为主
- 原型 / MVP:起步阶段,规模真大了再迁
- 测试数据库:
:memory:模式秒级启动
生产案例(不是玩具):
| 公司 / 产品 | SQLite 用法 |
|---|---|
| iOS / Android | 系统通讯录 / 浏览器 / 应用本地存储 |
| Chrome / Firefox / Safari | 历史记录 / 收藏夹 / 表单 |
| Notion 桌面 | 本地缓存 + 离线编辑 |
| ChatGPT 桌面 | 对话历史 |
| Slack 桌面 | 消息缓存 |
| Expensify | 后端核心账务库(高 TPS) |
| PocketBase / Turso / Cloudflare D1 / fly.io LiteFS | 围绕 SQLite 建的现代后端栈 |
并发瓶颈:写 vs 读
SQLite 最常被误解的就是”它不能并发”。准确说法:读并发几乎无限,写并发严重受限。
| 模式 | 读并发 | 写并发 | 推荐 |
|---|---|---|---|
| journal_mode=DELETE(默认) | 1(读阻塞写) | 1 | 不推荐 |
| journal_mode=WAL | 无限 | 1(写者串行) | 生产必用 |
WAL(Write-Ahead Log)模式的关键特性:
- 读不阻塞写、写不阻塞读
- 写者仍然只能 1 个,但读者可以无数个
- 单写者下,中等硬件 + 批量事务能跑 5000-50000 写/秒
典型并发上限:
- 写 < 100 TPS:SQLite 完全够,零调优
- 写 100-1000 TPS:WAL +
synchronous=NORMAL+ 合理 PRAGMA - 写 1000-5000 TPS:极限区,要批量事务(一个 BEGIN…COMMIT 包多条)+ 仔细调优
- 写 > 5000 TPS:迁 Postgres / MySQL 或分库
读 TPS:单机 SQLite 读可达 10 万-100 万 QPS(缓存命中时),瓶颈在 CPU 和磁盘 IO,不是 SQLite 本身。
SQLite vs PostgreSQL 的本质差异
| 维度 | SQLite | PostgreSQL |
|---|---|---|
| 部署 | 进程内库,单文件 | 独立服务,TCP 监听 |
| 并发模型 | 写串行(1 writer) | MVCC 多写者并行 |
| 类型系统 | 动态(type affinity) | 强类型 |
| 默认隔离级别 | SERIALIZABLE | READ COMMITTED |
| 复制 / 高可用 | 第三方(Litestream / LiteFS / rqlite) | 内置流复制 + 逻辑复制 |
| 用户权限 | 无(应用层做 RBAC) | GRANT / 角色系统 |
| 存储过程 | 不支持 | 支持(PL/pgSQL 等) |
| 扩展 | 内置 FTS5 / R-Tree / JSON1 | PostGIS / TimescaleDB / pgvector 等 |
| 运维成本 | 几乎为零 | 需要 DBA / 监控 |
核心选择规则:
- 嵌入式 / 单机服务 / 桌面 / 移动 → SQLite
- 多机集群 / 高并发写 / 需要复制 / 复杂权限 → Postgres / MySQL
单文件能大到多少
理论上限 281 TB(默认配置 16 TB),实际几百 GB 完全可用。
大库的注意事项:
- VACUUM 慢——大库重建索引要小时级,用
PRAGMA auto_vacuum=INCREMENTAL+ 定期PRAGMA incremental_vacuum - 必须 hot backup——
.backup命令或VACUUM INTO,不能直接 cp - WAL 文件可能很大——长事务不 commit 让 WAL 无限增长,定期
wal_checkpoint - mmap_size 设大点——让 OS 自动管理缓存
- 索引内存占用——百万行表索引可能几 GB,cache_size 要够
详细的 PRAGMA 调优另起一篇讲。
备份:必须用 SQLite 自己的机制
最常见的错误是 cp main.db backup.db——写入过程中复制会拿到不一致的页,恢复时报 “database disk image is malformed”。
正确方法:
| 方法 | 用法 | 适合 |
|---|---|---|
.backup 命令 | sqlite3 main.db ".backup backup.db" | CLI 一次性热备 |
VACUUM INTO | VACUUM INTO 'path/backup.db' | 输出紧凑库(3.27+) |
sqlite3_backup_* API | 编程接口,增量复制 | 嵌入式应用 |
| Litestream | 持续把 WAL 推 S3 / B2,秒级 RPO | 生产 Web 服务 |
| LiteFS | 文件系统层多机复制 | 多副本部署 |
陷阱:
- 多进程读写时简单 cp 几乎一定坏
- 备份完一定
PRAGMA integrity_check验证 - 加密库(SQLCipher)备份要带原密钥重建
- 不要依赖 LVM / ZFS 快照——除非快照前 fsync + 加全局写锁
5 类不适合 SQLite 的场景
- 多机分布式集群 —— rqlite / Dqlite / LiteFS 都是绕弯,原生场景用 Postgres / TiDB
- 高并发写(> 5000 TPS) —— 单 writer 锁是物理瓶颈,秒杀 / 高频交易不要用
- 多租户 SaaS 的强权限隔离 —— SQLite 没有 GRANT,靠应用层做 RBAC,复杂场景用 Postgres
- 复杂存储过程 / 触发器 —— SQLite 触发器有限,没有存储过程
- 跨数据中心同步 —— Postgres + 物理复制更省心
灰色区:
- 大数据分析 → 用 DuckDB / ClickHouse 更快(DuckDB 详见 DuckDB vs SQLite vs Pandas)
- 时序数据 → 能用但 TimescaleDB / InfluxDB 更专
- 图数据库 → CTE 能玩但性能差
决策清单
不确定选 SQLite 还是 Postgres 时过一遍:
- 估算真实写 TPS(不是”在线用户数”);< 100 TPS 直接 SQLite
- 是否需要多机?需要 → 选 Postgres 或考虑 Litestream / LiteFS
- 是否需要数据库层权限?需要 → Postgres
- 是否需要存储过程 / 触发器复杂逻辑?需要 → Postgres / MySQL
- 数据量预估 < 100 GB → SQLite 无障碍;> 1 TB → 考虑 Postgres 或分库
- 团队是否有 DBA 资源?没有 → 优先 SQLite(运维成本几乎为零)
- 不确定就先 SQLite,遇到瓶颈再迁——比反过来便宜得多
SQLite 不和 Postgres 竞争。它取代的是”fopen() + 自己造存储格式”。在它的目标场景里,是 Postgres 不能替代的。