SQLite 顶不住了?本地轻量服务该怎么选数据库 最近手头几个本地轻量服务SQLite 打底在并发小写入的场景下都开始喘。但这玩意是本地轻量服务不能塞一堆重依赖PG/MySQL 那一套直接 pass。借着给两个具体场景做选型的机会我把市面上还能用的本地轻量方案摸了一遍把结论和判断过程记下来也算给你做决策时省点查资料的时间。两个场景长这样一个局域网 CMDB监控 1 万多个 IoT 嵌入式服务的端口心跳大约每 30 秒一跳存 7 天用来展示。局域网里几十路摄像头一直在录 30 秒一段的短视频攒了 30~90 天的量要处理。先说结论后面再展开为什么。先说结论瓶颈大概率不是 SQLite 本身是写法。1 万设备 × 30 秒 平均才 333 次写入/秒对批量写入的 SQLite 来说根本不算压力。绝大多数卡死都是因为每条心跳独立提交 synchronousFULL每次提交都 fsync 到磁盘 没开 WAL再加上并发写互相抢锁。先开 WAL、单写协程攒批往往零成本就能解决不用动架构。「单二进制」和「重依赖」是两回事。VictoriaMetrics、QuestDB、MinIO、NATS 都只是一个静态可执行文件往机器上丢一个 20~60MB 的 binary 就能跑没有 initdb、没有服务账号、没有后台守护进程。这跟装 PostgreSQL/MySQL 完全不是一回事。所以第一步先想清楚你能不能接受「多跑一个进程」。场景一是时序负载最对路的是时序库。CMDB 心跳本质是时间序列 高频小写入 时间范围查询VictoriaMetrics 单节点vmsingle最合适前面接 NATS JetStream 做缓冲能彻底把生产者和存储解耦。场景二是 Blob 问题不是数据库问题。30 秒一段的视频几十路攒 90 天可能是 5~15 TB这东西绝不能进库。用 MinIOS3 对象存储或者干脆哈希目录存文件系统数据库只存元数据路径、摄像头、时长、处理状态。这种元数据写入速率极低SQLite 管这个绰绰有余。一句话场景一 SQLite WAL 调优兜底、上量上 VictoriaMetrics可选 NATS 缓冲场景二 MinIO 存视频 SQLite 存元数据。全程不碰 PG/MySQL轻量约束守住。先别急着换库看看瓶颈到底在哪动手加任何组件之前先确认瓶颈的性质。我见过太多「SQLite 并发瓶颈」其实都是下面这些能改的写法不是 SQLite 的硬上限反模式实际发生了什么怎么改每条心跳独立 autocommit每次提交都触发一次 fsync磁盘 IOPS 成了天花板几十 TPS 就卡死攒批单写线程 队列每 1000 条一个事务journal_modeDELETEsynchronousFULL回滚日志 每事务刷盘写写互斥并发时疯狂SQLITE_BUSY改WALsynchronousNORMAL多线程共用一个连接SQLite 连接不是线程安全的串行化 锁竞争每线程/协程独立连接或单写者模型没设busy_timeout一冲突就报错而不是重试要么丢数据要么重试风暴PRAGMA busy_timeout5000万级设备全在 :00/:30 打点瞬时万级写入尖峰单连接扛不住设备端加随机 jitter±几秒 服务端批量缓冲说个我自己踩过的坑SQLite 在WAL 模式 批量事务下单写者持续 5 万~10 万次插入/秒是能跑到的。你的场景平均才 333 次/秒即便最坏情况下「万级尖峰」也只需在秒级内消化掉——这完全落在 SQLite 的能力圈里。所以我个人的建议是换库前务必先做这一轮调优大概率零成本解决。调优清单就这几个 PRAGMA连接建立后执行一次PRAGMA journal_modeWAL;-- 写前日志读不阻塞写可以并发读PRAGMA synchronousNORMAL;-- WAL 下只需 WAL 文件 fsync不必每事务刷盘PRAGMA busy_timeout5000;-- 遇锁等 5s 重试而不是立刻 SQLITE_BUSYPRAGMA wal_autocheckpoint2000;-- 控制 checkpoint 频率降低抖动PRAGMA temp_storeMEMORY;-- 写入侧单写协程 批量事务伪代码BEGIN;INSERTINTOheartbeat(device_id,ts,port,status,latency)VALUES(?,?,?,?,?);-- × 1000一次 COMMITCOMMIT;两个场景的负载画像先算清楚账选型之前得先知道自己在扛多大的量不然容易被厂商宣传带跑。场景一局域网 IoT 心跳监控CMDB指标值被监控的 IoT 服务数10,000平均心跳写入速率~333/s7 天滚动窗口内行数~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,88020 路 × 30 天 的片段数~5.2M视频体量5~15 TB看路数和天数这俩场景本质不一样别混为一谈。场景一是「高频小写入 时间查询」走时序库或批量 SQLite场景二是「海量大文件 低频元数据」走对象存储 轻量元数据库。把 30 秒视频塞进任何 SQL/NoSQL 的「值」里结果都是数据库膨胀、备份灾难、查询变慢。视频进对象存储数据库只管元数据这是铁律。产品全景按「需不需要独立进程」分成两条主线。先泼盆冷水几乎所有「嵌入式」方案本质仍是单写者真要多个进程同时高频写必须走单二进制服务或者更重的分布式层而分布式层写也是串行的只为高可用。A. 先优化 SQLite零迁移SQLiteWAL 调优—— 单写者 WAL 并发读单文件.db零依赖场景一调优后够用场景二元数据首选。先做这步我估计 80% 的情况不用换库。B. 嵌入式库进程内LMDB—— MVCC读永不阻塞写、写永不阻塞读单写者mmap 直读读性能极强OpenLDAP 同款。适合读多写少、last-value 点查。Key-Value没 SQL。RocksDB—— LSM-Tree为写入优化单写者但写吞吐极高Kafka/TiKV 底层同款思路。适合进程内写洪流但 C 依赖偏重、没 SQL。bboltGo/ sledRust—— 单写 多读如果你的服务本身是 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. 单二进制服务sidecarVictoriaMetricsvmsingle—— 场景一首选。为时序写入高度优化单节点就能吃下百万级 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场景一·心跳场景二·视频关键备注SQLiteWAL 调优嵌入式单写者 WAL 并发读零依赖·单文件高极高先调优多数情况够用LMDB嵌入式 KVMVCC读不阻塞写单写单 C 库中极高读性能极强写吞吐一般RocksDB嵌入式 KVLSM高写吞吐单写C 库高中写洪流进程内首选bbolt / sled嵌入式 KV单写 多读Go / Rust 库中高按技术栈选DuckDB嵌入式 OLAP进程内并发写append 无冲突多进程只读单 C 库·零依赖中不适用分析查询强非主存储libSQLSQLite fork单写者继承嵌入式 HTTP server中高加副本/远程不改写并发rqlite / dqlite分布式 SQLite写串行过 leaderHA单二进制Go/ C 库可用中为高可用非写吞吐VictoriaMetrics单二进制 TSDB高并发写 列存压缩单个静态二进制极高不适用场景一首选QuestDB / InfluxDB单二进制 TSDB高并发写单二进制极高不适用场景一备选NATS JetStream单二进制 消息高吞吐 pub/sub 持久单二进制高中做写入缓冲 / 事件总线MinIO单二进制 对象存储高吞吐 blob单二进制不适用极高场景二视频归宿决策树照着走就行不用硬记是否 / 已调优仍不够能接受推荐能接受推荐必须纯进程内零额外进程写洪流读重 / last-value视频元数据需要单机即可瓶颈是架构层还是 SQLite 硬伤每条都 autocommit FULL 无 WAL§1 WAL 调优单写攒批 busy_timeout能否接受多跑一个单二进制 sidecar场景一VictoriaMetrics 可选 NATS 缓冲场景二MinIO SQLite 元数据读写特征RocksDB / DuckDB / SQLiteWALLMDBSQLite / LMDB / bbolt 文件系统需要多机 HA / 容灾rqlite / dqlite提升可用性写仍串行前述选型直接落地分场景推荐架构场景一IoT 心跳监控10,000 IoT 设备每 30s 心跳NATS JetStream持久化缓冲 · 削峰填谷VictoriaMetrics vmsingle单二进制 · 7d 留存DashboardGrafana / 自研设备心跳 → NATS JetStream持久化、抗尖峰→ 消费者批量写入 VictoriaMetricsvmsingle-retentionPeriod7→ 仪表盘用 MetricsQL 查。心跳写入走 InfluxDB 行协议heartbeat,deviceiot-001,port8080 status1,latency_ms12不想引入 NATS 的话可以简化成「设备直接批量写 VM」或者退回「SQLite WAL 调优」兜底。我倾向于小流量先 SQLite 顶着真看到瓶颈再上 VM没必要一上来就铺。场景二摄像头短视频拉取视频摄像头每 30s 一段MinIO S3 对象存储key: cam/date/time.mp4ILM 自动过期 30-90 天处理 Worker转码 / 识别SQLite / LMDB 元数据路径/摄像头/时长/状态CMDB 面板只查元数据摄像头写 30s 片段 →MinIOS3key 按摄像头/日期/时间.mp4组织配生命周期规则自动删 30~90 天前的片段→ 处理 Worker 拉视频做转码 / 识别 → 结果缩略图、检测结果回写 →SQLite 存元数据路径、摄像头、时长、处理状态。面板只查元数据绝不扫视频本体。落地配置示例① SQLite WAL 调优零成本兜底两场景通用-- 连接初始化各语言 SDK 都支持 PRAGMAPRAGMA journal_modeWAL;PRAGMA synchronousNORMAL;PRAGMA busy_timeout5000;-- 写入单写协程 每批 1000 条一个事务-- 设备端打点加 jittersleep(random(0, 4000))ms错峰② VictoriaMetrics 单节点场景一上量方案# 下载单个二进制后直接运行./victoria-metrics-prod-retentionPeriod7-storageDataPath./vmdata# 设备/网关用 InfluxDB 行协议写入HTTP POST :8428/writeheartbeat,deviceiot-001,port8080status1,latency_ms121717833600000000000# 查询最新状态MetricsQLlast_over_time(heartbeat{deviceiot-001}[5m])# 趋势图自动降采样count_over_time(heartbeat[1h])③ MinIO 对象存储场景二视频归宿# 单个二进制起服务minio server /data/videos --console-address :9001# 上传对象 key 即元数据里的 pathmccpclip_20260708_120000.mp4 local/cam-01/2026/07/08/# 元数据表SQLite / LMDB 均可CREATE TABLE clip_meta(idINTEGER PRIMARY KEY, cam TEXT, start_ts INTEGER, end_ts INTEGER, obj_key TEXT, size INTEGER, status TEXT);落地清单先量后换上线 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 官方文档libSQL GitHubVictoriaMetrics GitHubDuckDB 并发模型MinIO 官网NATS JetStream 文档QuestDB 官网LMDB (Symas)基于公开产品文档与社区实践整理2026-07。产品能力持续演进落地前请以各项目最新官方文档为准。