引子:一个看似不可能的命题
SQLite 是世界上最流行的数据库。它运行在每一部智能手机、每一台电脑、无数个嵌入式设备里。它小巧、快速、可靠,有一个致命弱点:它是单机的。
如果你需要高可用性,传统答案是:放弃 SQLite,去用 PostgreSQL、MySQL、或者更重的分布式数据库如 CockroachDB。
但 Philip O'Toole 在 2014 年提出了一个疯狂的问题:如果保留 SQLite 的一切优点,只给它加上分布式共识能力,会怎样?
10 年后,rqlite 已经是一个成熟的生产级项目:
- 14,000+ GitHub stars
- 完整的 Raft 共识实现
- 支持 66 种语言的客户端
- 被用于从边缘设备到云原生系统的各种场景
---
第一部分:问题的本质——为什么分布式这么难?
在理解 rqlite 的解决方案之前,我们需要理解问题的本质。
CAP 定理的阴影
分布式系统的铁律:一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance),三者最多取其二。
大多数现代系统选择了可用性 + 分区容错性(AP),牺牲强一致性。这意味着你的数据可能暂时不一致,但最终会收敛。
但对于关键数据——配置信息、账户余额、库存数量——你需要强一致性。你不能接受"最终一致",你需要知道写入成功后,所有节点都看到了同样的数据。
共识算法:让分布式系统达成一致
实现强一致性的标准答案是共识算法。最著名的两个:
Paxos(1990s):理论上优雅,实践中难以理解和实现。Google 的 Chubby、ZooKeeper 都基于此。
Raft(2014):Stanford 的 Diego Ongaro 和 John Ousterhout 设计的 Paxos 替代品。核心设计目标是可理解性。
Raft 的工作原理可以概括为: 1. 领导者选举:集群选出一个 Leader 处理所有写入 2. 日志复制:Leader 把写入操作追加到日志,复制到多数节点后才确认提交 3. 安全性:只有包含已提交日志条目的节点才能当选 Leader
关键保证:只要多数节点存活,系统就能继续工作。
---
第二部分:rqlite 的核心直觉——不要做太多
rqlite 的设计哲学非常克制:只做一件事,把它做好。
架构分层
rqlite 由三个清晰分离的子系统组成:
┌─────────────────────────────────────────────┐
│ HTTP API Layer │ ← 对外接口
├─────────────────────────────────────────────┤
│ Raft Consensus Layer │ ← 分布式共识
├─────────────────────────────────────────────┤
│ SQLite Database │ ← 存储引擎
└─────────────────────────────────────────────┘
HTTP 层:处理 REST API 请求,返回 JSON 结果。支持批量操作、事务、参数化查询。
Raft 层:基于 HashiCorp 的 Raft 实现。处理领导者选举、日志复制、快照、成员变更。
SQLite 层:嵌入式的 SQLite 数据库。每个节点都有一个完整的副本。
写入路径:强一致性的保证
当客户端发送写入请求时:
1. 如果请求发送到 Leader:
- Leader 将操作追加到 Raft 日志
- 异步复制到所有 Follower
- 等待多数节点确认(quorum)
- 提交到本地 SQLite
- 返回成功给客户端
- Follower 返回 301 重定向到 Leader
- 或(可选)自动转发到 Leader
读取路径:灵活的一致性级别
rqlite 提供三种读取一致性选项:
| 级别 | 行为 | 适用场景 |
|---|---|---|
| none | 直接从本地 SQLite 读 | 对延迟敏感、可接受 stale 数据 |
| weak | 确认自己是 Leader 后读 | 需要新鲜数据但可容忍小段不一致 |
| strong | Leader 确认多数节点已应用日志 | 需要强一致性 |
---
第三部分:技术细节——优雅的工程实践
为什么选择 SQLite?
rqlite 不是第一个尝试分布式的 SQLite 项目。但它的选择非常精准:
SQLite 的优势:
- 零配置、零管理
- 单文件存储
- 事务支持(ACID)
- 全文搜索、JSON、GIS 扩展
- 经过数十亿设备的实战检验
- Raft 共识层
- HTTP API 包装
- 自动集群管理
集群管理:比 etcd 还简单
rqlite 的集群形成非常简单:
# 第一个节点(等待其他节点加入)
rqlited -node-id node1 -http-addr 0.0.0.0:4001 -raft-addr 0.0.0.0:4002 \
-bootstrap-expect 3 /var/lib/rqlite
# 第二个节点(加入集群)
rqlited -node-id node2 -http-addr 0.0.0.0:4001 -raft-addr 0.0.0.0:4002 \
-join http://node1:4001 /var/lib/rqlite
# 第三个节点(加入集群)
rqlited -node-id node3 -http-addr 0.0.0.0:4001 -raft-addr 0.0.0.0:4002 \
-join http://node1:4001 /var/lib/rqlite
集群自动完成领导者选举、日志同步、故障检测。
自动发现:Kubernetes 原生支持
rqlite 支持多种服务发现机制:
- Kubernetes:通过 DNS 或 Headless Service 自动发现
- Consul:服务注册与发现
- etcd:复用现有的键值存储
- DNS:基于 DNS SRV 记录
- 静态配置:直接指定节点地址
备份与恢复:云原生设计
# 热备份到 S3
rqlite -auto-backup s3://bucket/path
# 从 S3 恢复
rqlite -auto-restore s3://bucket/path
支持 AWS S3、MinIO、Google Cloud Storage。备份在后台进行,不影响服务。
---
第四部分:与同类项目的对比
| 特性 | rqlite | etcd | CockroachDB | dqlite |
|---|---|---|---|---|
| 存储引擎 | SQLite | BoltDB | 自定义 | SQLite |
| 数据模型 | 关系型 | 键值 | 关系型 | 关系型 |
| SQL 支持 | 完整 | 无 | 完整 | 完整 |
| 部署复杂度 | 极简 | 简单 | 复杂 | 简单 |
| 水平扩展 | 读扩展(只读节点) | 读扩展 | 读写扩展 | 读扩展 |
| 数据规模 | GB 级 | GB 级 | TB+ 级 | GB 级 |
| 适用场景 | 配置数据、边缘计算 | 服务发现、配置 | 大规模 OLTP | 嵌入式/IoT |
rqlite vs etcd
两者都使用 Raft,都定位为"分布式配置存储"。关键区别:
- etcd:纯键值,无 SQL,适合服务发现
- rqlite:关系型 + SQL,适合结构化配置数据
> "etcd 的索引代码是 bespoke code。每次别人要碰它,我都得解释。我们需要更容易上手的东西。"
rqlite vs CockroachDB
两者都提供分布式 SQL,但设计目标完全不同:
- rqlite:简单第一,适合 GB 级数据,运维成本极低
- CockroachDB:企业级,TB+ 级数据,完整的分布式事务
rqlite vs dqlite
dqlite(distributed SQLite)是 Canonical 的项目,用于 Ubuntu 的分布式系统。两者非常相似,主要区别:
- rqlite:Go 编写,HTTP API,更广泛的语言支持
- dqlite:C 编写,C/Raft 实现,与 LXD 深度集成
第五部分:使用场景——rqlite 适合什么?
场景 1:分布式配置管理
把 rqlite 当作"带 SQL 的 etcd":
-- 存储服务配置
CREATE TABLE services (
name TEXT PRIMARY KEY,
endpoint TEXT,
health_check_interval INTEGER,
max_retries INTEGER
);
-- 存储 feature flags
CREATE TABLE feature_flags (
flag_name TEXT PRIMARY KEY,
enabled BOOLEAN,
rollout_percentage INTEGER
);
高可用、强一致、支持复杂查询。
场景 2:边缘计算与 IoT
rqlite 的轻量级使其成为边缘设备的理想选择:
- 单二进制文件,<100MB 内存占用
- 自动故障转移
- 断网时本地 SQLite 仍可工作
- 恢复后自动同步
场景 3:云原生应用的轻量级状态存储
不需要 Kafka + PostgreSQL + Redis 的复杂栈:
- 用户会话:关系型存储,支持过期
- 任务队列:利用 SQLite 的 ACID 保证
- 指标数据:配合全文搜索和 JSON 扩展
场景 4:开发与测试环境
生产用 PostgreSQL,开发测试用 rqlite:
- 完全兼容 SQL
- 秒级启动
- 无需 Docker Compose 编排
第六部分:rqlite 9.0 的新特性——Change Data Capture
2024 年发布的 rqlite 9.0 引入了 CDC(变更数据捕获),这是一个重大进化。
什么是 CDC?
CDC 自动捕获数据库变更事件(INSERT、UPDATE、DELETE),推送到外部系统。
// CDC 事件示例
{
"table": "users",
"operation": "INSERT",
"row_id": 42,
"new_values": {
"id": 42,
"name": "Alice",
"email": "alice@example.com"
}
}
为什么这很重要?
CDC 让 rqlite 从"被动存储"变成"事件源":
- 实时同步到 Elasticsearch 做搜索
- 触发 webhook 通知其他服务
- 构建事件溯源(Event Sourcing)架构
- 数据管道:rqlite → Kafka → 数据仓库
实现挑战
CDC 在 SQLite 上并不容易实现,因为 SQLite 没有内置的变更流机制。rqlite 的解决方案:
- 在 Raft 日志层面拦截变更
- 异步推送到配置的 HTTP 端点
- 支持批处理和重试
第七部分:10 年演进——从原型到生产
rqlite 从 2014 年到 2024 年的演进展示了优秀开源项目的成长轨迹:
2014:起步
- Philip O'Toole 开始项目
- 基本 Raft + SQLite 集成
2016:共识层升级
- 从自定义 Raft 实现迁移到 HashiCorp Raft
- 大幅提升稳定性和性能
2017:发现服务
- AWS Lambda 驱动的节点发现
- 自动集群形成
2020:Protocol Buffers
- 内部数据结构从 JSON 迁移到 Protobuf
- 更高效的节点间通信
2021:重大架构重构
- 6.0 版本重构节点间通信
- 移除脆弱的 HTTP URL 映射机制
- 引入多路复用的逻辑连接
2022:Jepsen 测试
- 引入 Jepsen-style 一致性测试
- 验证线性一致性保证
2023:大数据集支持
- 优化 GB 级数据集的处理
- 更快的重启速度
2024:CDC 与 10 周年
- 9.0 引入 Change Data Capture
- Philip 撰写"10 年分布式系统开发观察"
尾声:简单性的胜利
rqlite 的成功不是因为它做了最多的事情,而是因为它拒绝做太多事情。
它没有去重新发明存储引擎(用 SQLite),没有去重新发明共识算法(用 Raft),没有去重新发明查询语言(用 SQL)。
它只是把这些经过验证的技术,用正确的方式组合在一起,然后提供了一个极其简单的接口。
在这个追求"云原生"、"微服务"、"中台"的时代,rqlite 提醒我们:简单性本身就是一种特性。
正如 Philip O'Toole 在 10 周年博客中写的:
> "分布式系统很难。但如果你想理解它们是如何工作的,rqlite 是一个很好的例子。它的设计和实现经过了深思熟虑,各个组件之间有清晰的分离。"
---
参考链接
- rqlite 官网: https://rqlite.io/
- GitHub: https://github.com/rqlite/rqlite
- 快速开始: https://rqlite.io/docs/quick-start/
- 设计文档: https://rqlite.io/docs/design/
- Raft 论文: https://raft.github.io/raft.pdf
- Philip O'Toole 的博客: https://philipotoole.com/
- 10 周年回顾: https://philipotoole.com/rqlite-turns-10-observations-from-a-decade-building-distributed-systems/
后记:一个二进制文件的哲学
我特别喜欢 rqlite 的部署体验:
curl -L https://github.com/rqlite/rqlite/releases/latest/download/rqlited-linux-amd64 \
-o rqlited
chmod +x rqlited
./rqlited ~/node.1
一个文件,一个命令,一个高可用的分布式数据库。
这让我想起 Unix 哲学:做一件事,把它做好;组合小工具解决大问题。
rqlite 没有试图成为下一个 CockroachDB 或 Spanner。它只想成为"高可用的 SQLite"。而这,恰恰是它最迷人的地方。
---
*写于 2026 年 4 月 5 日,参考 rqlite 官方文档、Philip O'Toole 的技术博客及 Raft 共识算法论文。*