软件选型 · 2026-07 整理
本地轻量数据库选型
SQLite 顶不住时的替代路线:瓶颈定位方法、嵌入式库与衍生复制层横评、两个典型负载的选型结论。
最近手头几个本地轻量服务(SQLite 打底)在并发小写入的场景下都开始喘。但这玩意是本地轻量服务,不能塞一堆重依赖,PG/MySQL 那一套直接 pass。借着给两个具体场景做选型的机会,我把市面上还能用的本地轻量方案摸了一遍,把结论和判断过程记下来,也算给你做决策时省点查资料的时间。
两个场景长这样:
- 一个局域网 CMDB,监控 1 万多个 IoT 嵌入式服务的端口心跳,大约每 30 秒一跳,存 7 天用来展示。
- 局域网里几十路摄像头,一直在录 30 秒一段的短视频,攒了 30~90 天的量要处理。
先说结论,后面再展开为什么。
先说结论
- 瓶颈大概率不是 SQLite 本身,是写法。 1 万设备 × 30 秒 = 平均才 333 次写入/秒,对批量写入的 SQLite 来说根本不算压力。绝大多数卡死都是因为每条心跳独立提交 + synchronous=FULL(每次提交都 fsync 到磁盘)+ 没开 WAL,再加上并发写互相抢锁。先开 WAL、单写协程攒批,往往零成本就能解决,不用动架构。
- 「单二进制」和「重依赖」是两回事。 VictoriaMetrics、QuestDB、MinIO、NATS 都只是一个静态可执行文件,往机器上丢一个 20~60MB 的 binary 就能跑,没有 initdb、没有服务账号、没有后台守护进程。这跟装 PostgreSQL/MySQL 完全不是一回事。所以第一步先想清楚:你能不能接受「多跑一个进程」。
- 场景一是时序负载,最对路的是时序库。 CMDB 心跳本质是时间序列 + 高频小写入 + 时间范围查询,VictoriaMetrics 单节点(vmsingle)最合适,前面接 NATS JetStream 做缓冲能彻底把生产者和存储解耦。
- 场景二是 Blob 问题,不是数据库问题。 30 秒一段的视频,几十路攒 90 天可能是 5~15 TB,这东西绝不能进库。用 MinIO(S3 对象存储)或者干脆哈希目录存文件系统,数据库只存元数据(路径、摄像头、时长、处理状态)。这种元数据写入速率极低,SQLite 管这个绰绰有余。
一句话:场景一 SQLite WAL 调优兜底、上量上 VictoriaMetrics(可选 NATS 缓冲);场景二 MinIO 存视频 + SQLite 存元数据。 全程不碰 PG/MySQL,轻量约束守住。
先别急着换库,看看瓶颈到底在哪
动手加任何组件之前,先确认瓶颈的性质。我见过太多「SQLite 并发瓶颈」其实都是下面这些能改的写法,不是 SQLite 的硬上限:
| 反模式 | 实际发生了什么 | 怎么改 |
|---|---|---|
| 每条心跳独立 autocommit | 每次提交都触发一次 fsync,磁盘 IOPS 成了天花板,几十 TPS 就卡死 | 攒批:单写线程 + 队列,每 1000 条一个事务 |
| journal_mode=DELETE + synchronous=FULL | 回滚日志 + 每事务刷盘,写写互斥,并发时疯狂 SQLITE_BUSY | 改 WAL + synchronous=NORMAL |
| 多线程共用一个连接 | SQLite 连接不是线程安全的,串行化 + 锁竞争 | 每线程/协程独立连接,或单写者模型 |
| 没设 busy_timeout | 一冲突就报错而不是重试,要么丢数据要么重试风暴 | PRAGMA busy_timeout=5000 |
| 万级设备全在 :00/:30 打点 | 瞬时万级写入尖峰,单连接扛不住 | 设备端加随机 jitter(±几秒)+ 服务端批量缓冲 |
说个我自己踩过的坑:SQLite 在 WAL 模式 + 批量事务下,单写者持续 5 万~10 万次插入/秒是能跑到的。你的场景平均才 333 次/秒,即便最坏情况下「万级尖峰」也只需在秒级内消化掉——这完全落在 SQLite 的能力圈里。所以我个人的建议是,换库前务必先做这一轮调优,大概率零成本解决。
调优清单就这几个 PRAGMA,连接建立后执行一次:
两个场景的负载画像(先算清楚账)
选型之前得先知道自己在扛多大的量,不然容易被厂商宣传带跑。
场景一:局域网 IoT 心跳监控(CMDB)
| 指标 | 值 |
|---|---|
| 被监控的 IoT 服务数 | 10,000+ |
| 平均心跳写入速率 | ~333/s |
| 7 天滚动窗口内行数 | ~2.0 亿 |
| 列式压缩后存储 | 2~4 GB(原始约 16GB) |
算法:1 万服务 ×(每 30s 一次)= 333 次/秒;单日 1 万 × 2880(每天 30s 间隔数)= 2880 万行;7 天 ≈ 2.0 亿行。每行(设备ID + 时间戳 + 端口 + 状态 + 延迟)约 80 字节 → 原始约 16GB,时序库列存压缩后一般压到 2~4GB。
查询特征大致三类:① 每个服务「最新一次心跳」状态(last-value);② 时间范围趋势图(需要降采样);③ 7 天自动过期(retention)。
场景二:局域网摄像头短视频
| 指标 | 值 |
|---|---|
| 单段视频时长 | 30s |
| 单摄像头 / 天 段数 | 2,880 |
| 20 路 × 30 天 的片段数 | ~5.2M |
| 视频体量 | 5~15 TB(看路数和天数) |
这俩场景本质不一样,别混为一谈。场景一是「高频小写入 + 时间查询」,走时序库或批量 SQLite;场景二是「海量大文件 + 低频元数据」,走对象存储 + 轻量元数据库。把 30 秒视频塞进任何 SQL/NoSQL 的「值」里,结果都是数据库膨胀、备份灾难、查询变慢。视频进对象存储,数据库只管元数据,这是铁律。
产品全景
按「需不需要独立进程」分成两条主线。先泼盆冷水:几乎所有「嵌入式」方案本质仍是单写者,真要多个进程同时高频写,必须走单二进制服务或者更重的分布式层(而分布式层写也是串行的,只为高可用)。
A. 先优化 SQLite(零迁移)
- SQLite(WAL 调优) —— 单写者 + WAL 并发读;单文件 .db,零依赖;场景一调优后够用,场景二元数据首选。先做这步,我估计 80% 的情况不用换库。
B. 嵌入式库(进程内)
- LMDB —— MVCC:读永不阻塞写、写永不阻塞读;单写者,mmap 直读,读性能极强(OpenLDAP 同款)。适合读多写少、last-value 点查。Key-Value,没 SQL。
- RocksDB —— LSM-Tree,为写入优化,单写者但写吞吐极高(Kafka/TiKV 底层同款思路)。适合进程内写洪流,但 C++ 依赖偏重、没 SQL。
- bbolt(Go)/ sled(Rust) —— 单写 + 多读;如果你的服务本身是 Go / Rust 栈,直接引库最顺手。
- DuckDB —— 单进程内可读写并发(无冲突的写,尤其 append,可以并行);多进程写入不支持。列式引擎,分析查询、降采样、报表极佳,不适合做高频并发写入的主存储。
C. SQLite 衍生 / 复制层
- libSQL —— SQLite 的 fork,继承了单写者模型(官方明确:并发写入是 Turso 另一套 Rust 重写的数据库才解决)。新增了嵌入式副本、HTTP Server 远程访问。它不解决写并发瓶颈,只有当你需要「SQLite + 边缘副本 / 远程访问」时才值得换。别被「SQLite fork」这五个字骗去治并发。
- rqlite / dqlite —— 通过 Raft 做复制与高可用;写入仍要经过 leader 串行化——提升的是可用性 / 读扩展,不是写吞吐。需要多机 HA/容灾时才上。
- Litestream —— 后台进程把 SQLite 变更增量复制到文件 / S3,是容灾备份,不是并发方案。
D. 单二进制服务(sidecar)
- VictoriaMetrics(vmsingle) —— 场景一首选。为时序写入高度优化,单节点就能吃下百万级 samples/s;列存压缩,7 天 retention 自动过期。一个静态二进制 ./victoria-metrics-prod,无外部依赖;兼容 InfluxDB / Prometheus / OpenTSDB / JSON 行协议。
- QuestDB / InfluxDB v3 —— 同为单二进制时序库;QuestDB 用 SQL + InfluxDB 行协议、ingestion 极快;InfluxDB 生态成熟。场景一备选。
- MinIO —— 场景二首选。自托管 S3 兼容对象存储,一个二进制起服务;为海量文件设计,支持生命周期过期(正好匹配 30~90 天留存)。视频文件的归宿,配合 SQLite/LMDB 存元数据。
- NATS JetStream —— 单二进制消息系统,JetStream 提供持久化、at-least-once、重放。场景一的写入缓冲层(设备 → JetStream → 批量落库);也能做场景二「摄像头 → 存储 → 处理」的事件通知总线。
E. 更重(为什么不选)
- ClickHouse —— 分析能力炸裂,但对你的规模偏重,且偏 OLAP 而非实时 ingestion;场景一若要做复杂聚合报表可备选。
- TimescaleDB / PostgreSQL / MySQL —— 需要运行时 + 初始化 + 服务账号,违反「不能装太多依赖」的约束,列出来只是说明为什么 pass。
横向对比
评分用文字,避免看图说话:极高 / 高 / 中 / 可用 / 偏弱 / 不适用。
| 产品 | 类型 | 并发模型 | 部署 footprint | 场景一·心跳 | 场景二·视频 | 关键备注 |
|---|---|---|---|---|---|---|
| SQLite(WAL 调优) | 嵌入式 | 单写者 + WAL 并发读 | 零依赖·单文件 | 高 | 极高 | 先调优,多数情况够用 |
| LMDB | 嵌入式 KV | MVCC,读不阻塞写,单写 | 单 C 库 | 中 | 极高 | 读性能极强,写吞吐一般 |
| RocksDB | 嵌入式 KV | LSM,高写吞吐,单写 | C++ 库 | 高 | 中 | 写洪流进程内首选 |
| bbolt / sled | 嵌入式 KV | 单写 + 多读 | Go / Rust 库 | 中 | 高 | 按技术栈选 |
| DuckDB | 嵌入式 OLAP | 进程内并发写(append 无冲突),多进程只读 | 单 C 库·零依赖 | 中 | 不适用 | 分析查询强,非主存储 |
| libSQL | SQLite fork | 单写者(继承) | 嵌入式 + HTTP server | 中 | 高 | 加副本/远程,不改写并发 |
| rqlite / dqlite | 分布式 SQLite | 写串行过 leader(HA) | 单二进制(Go)/ C 库 | 可用 | 中 | 为高可用,非写吞吐 |
| VictoriaMetrics | 单二进制 TSDB | 高并发写 + 列存压缩 | 单个静态二进制 | 极高 | 不适用 | 场景一首选 |
| QuestDB / InfluxDB | 单二进制 TSDB | 高并发写 | 单二进制 | 极高 | 不适用 | 场景一备选 |
| NATS JetStream | 单二进制 消息 | 高吞吐 pub/sub + 持久 | 单二进制 | 高 | 中 | 做写入缓冲 / 事件总线 |
| MinIO | 单二进制 对象存储 | 高吞吐 blob | 单二进制 | 不适用 | 极高 | 场景二视频归宿 |
决策树
照着走就行,不用硬记:
分场景推荐架构
场景一:IoT 心跳监控
设备心跳 → NATS JetStream(持久化、抗尖峰)→ 消费者批量写入 VictoriaMetrics(vmsingle,-retentionPeriod=7)→ 仪表盘用 MetricsQL 查。心跳写入走 InfluxDB 行协议:
不想引入 NATS 的话,可以简化成「设备直接批量写 VM」,或者退回「SQLite WAL 调优」兜底。我倾向于小流量先 SQLite 顶着,真看到瓶颈再上 VM,没必要一上来就铺。
场景二:摄像头短视频
摄像头写 30s 片段 → MinIO(S3,key 按 摄像头/日期/时间.mp4 组织,配生命周期规则自动删 30~90 天前的片段)→ 处理 Worker 拉视频做转码 / 识别 → 结果(缩略图、检测结果)回写 → SQLite 存元数据(路径、摄像头、时长、处理状态)。面板只查元数据,绝不扫视频本体。
落地配置示例
① SQLite WAL 调优(零成本兜底,两场景通用)
② VictoriaMetrics 单节点(场景一上量方案)
③ MinIO 对象存储(场景二视频归宿)
落地清单
- 先量后换: 上线 WAL 调优,用真实流量压 24 小时,确认 QPS / 延迟达标再决定要不要引 sidecar。
- 写入侧加缓冲: 不管选哪个库,入口都放一个内存队列 + 单写协程,把并发写变成串行批量写。
- 设备端错峰: 心跳时间戳加 ±几秒随机 jitter,消掉 :00/:30 的万级尖峰。
- 场景二绝不存视频进库: 视频只在 MinIO / 文件系统,库里只留 obj_key 字符串。
风险与权衡
老实说几个坑:
- 嵌入式 ≠ 多写者。 LMDB / RocksDB / DuckDB / libSQL / bbolt 本质都是单写者。所谓「并发」多是「多读 + 单写」或「进程内无冲突并发」。如果你真需要多个独立进程同时高频写,纯嵌入路线无解,得上单二进制服务(VM/QuestDB)或分布式层(rqlite,但写仍串行)。
- 单二进制也是要运维的。 VictoriaMetrics / MinIO 虽是一个文件,但仍是一个独立进程:要管启动、监控、磁盘、升级。好处是远比 PG/MySQL 轻——无运行时、无初始化、无账号体系。动手前先确认你的「不能装依赖」包不包含「可跑一个 sidecar」。
- 视频体量要算账。 20 路 × 90 天 ≈ 5~15 TB。确认磁盘容量和生命周期过期策略(MinIO ILM 或脚本清理),否则存储会悄悄涨满。冷数据可以迁到更大但更慢的盘。
- DuckDB 别当主库。 DuckDB 强在分析查询,多进程写入不支持。它适合做「SQLite/VM 存原始,DuckDB 做离线报表」,而不是 ingestion 主存储。
最后给个优先级:① 先 SQLite WAL 调优(零成本,两场景元数据都覆盖);② 场景一若上量,VictoriaMetrics(单二进制)+ 可选 NATS 缓冲;③ 场景二 MinIO + SQLite 元数据。全程不碰 PG/MySQL,轻量约束守住。
参考来源
- [SQLite WAL 官方文档](https://www.sqlite.org/wal.html)
- [libSQL GitHub](https://github.com/tursodatabase/libsql)
- [VictoriaMetrics GitHub](https://github.com/VictoriaMetrics/VictoriaMetrics)
- [DuckDB 并发模型](https://duckdb.org/docs/current/connect/concurrency)
- [MinIO 官网](https://min.io/)
- [NATS JetStream 文档](https://docs.nats.io/nats-concepts/jetstream)
- [QuestDB 官网](https://questdb.io/)
- [LMDB (Symas)](https://github.com/symas/lmdb)
*基于公开产品文档与社区实践整理(2026-07-08)。产品能力持续演进,落地前请以各项目最新官方文档为准。*