rqlite 完全指南 / 第 3 章:架构深度解析
第 3 章:架构深度解析
深入了解 rqlite 的内部架构,包括 Raft 共识层、SQLite 存储引擎和 HTTP API 层的设计细节。
3.1 整体架构
rqlite 的架构可以分为三层:
┌─────────────────────────────────────────────────────────┐
│ 客户端层 │
│ (curl / rqlite CLI / SDK / 浏览器) │
└──────────────────────┬──────────────────────────────────┘
│ HTTP/JSON
┌──────────────────────▼──────────────────────────────────┐
│ HTTP API 层 │
│ ┌───────────┐ ┌───────────┐ ┌──────────┐ ┌───────┐ │
│ │ /status │ │ /nodes │ │ /db/* │ │ /join │ │
│ └───────────┘ └───────────┘ └──────────┘ └───────┘ │
└──────────────────────┬──────────────────────────────────┘
│
┌──────────────────────▼──────────────────────────────────┐
│ rqlite 核心层 │
│ ┌─────────────────────────────────────────────┐ │
│ │ 查询/执行路由 │ │
│ │ - 查询路由: Follower/Leader 读分流 │ │
│ │ - 执行路由: 写请求强制到 Leader │ │
│ └─────────────────┬───────────────────────────┘ │
│ │ │
│ ┌─────────────────▼───────────────────────────┐ │
│ │ Raft 共识层 │ │
│ │ - Leader 选举 │ │
│ │ - 日志复制 │ │
│ │ - 成员变更 │ │
│ │ - 快照 (Snapshot) │ │
│ └─────────────────┬───────────────────────────┘ │
│ │ │
│ ┌─────────────────▼───────────────────────────┐ │
│ │ SQLite 存储引擎 │ │
│ │ - WAL 模式 (Write-Ahead Logging) │ │
│ │ - in-memory 或 on-disk │ │
│ │ - go-sqlite3 (CGO) │ │
│ └─────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
3.2 Raft 共识层详解
3.2.1 Leader 选举机制
rqlite 使用 hashicorp/raft 库实现 Raft 协议。Leader 选举是集群正常工作的基础。
选举流程:
时间线 ──────────────────────────────────────────►
Node1 (Leader) │███│███│███│███│ 崩溃! │ │███│███│
Node2 (Follower)│ │ │ │ │ 超时!→候选 │ 选为Leader│███│███│
Node3 (Follower)│ │ │ │ │ │ 投票给Node2│ │███│
心跳正常 选举超时 新 Leader
| 配置项 | 默认值 | 说明 |
|---|---|---|
HeartbeatTimeout | 1s | 心跳超时时间 |
ElectionTimeout | 1s | 选举超时时间 |
LeaderLeaseTimeout | 500ms | Leader 租约超时 |
SnapshotInterval | 120s | 快照触发间隔 |
SnapshotThreshold | 8192 | 触发快照的日志条目数 |
3.2.2 日志复制
当 Leader 收到写请求时,流程如下:
客户端 Leader Follower 1 Follower 2
│ │ │ │
│──── 写请求 ────────────►│ │ │
│ │ │ │
│ │── AppendEntries ─────────►│ │
│ │── AppendEntries ──────────────────────────────────►│
│ │ │ │
│ │◄── 确认 ──────────────────│ │
│ │◄── 确认 ─────────────────────────────────────────│
│ │ │ │
│ │ (多数确认,提交日志) │ │
│ │ │ │
│◄── 返回结果 ───────────│ │ │
│ │── AppendEntries (commit)──►│ │
│ │── AppendEntries (commit)──────────────────────────►│
3.2.3 快照(Snapshot)
随着日志增长,rqlite 会定期创建快照以压缩日志:
| 快照机制 | 说明 |
|---|---|
| 触发条件 | 日志条目数超过 SnapshotThreshold 或超过 SnapshotInterval |
| 快照内容 | 当前 SQLite 数据库文件的完整副本 |
| 增量传输 | 新节点加入时,通过快照快速同步状态 |
| 自动清理 | 旧的快照和日志在新快照创建后自动清理 |
查看当前快照状态:
curl -s 'localhost:4001/status?pretty' | python3 -c "
import json, sys
data = json.load(sys.stdin)
store = data.get('store', {})
print(f'Raft State: {store.get(\"raft_state\")}')
print(f'Last Log Index: {store.get(\"last_log_index\")}')
print(f'Last Snapshot Index: {store.get(\"last_snapshot_index\")}')
"
3.3 SQLite 存储引擎
3.3.1 存储模式
rqlite 支持两种 SQLite 存储模式:
| 模式 | 说明 | 适用场景 |
|---|---|---|
| in-memory(默认) | SQLite 数据库完全在内存中 | 数据量小、追求性能 |
| on-disk | SQLite 数据库写入磁盘 | 数据量大、需要持久化 |
# 使用 on-disk 模式启动
rqlited -on-disk /var/lib/rqlite/data
# 使用 in-memory 模式启动(默认)
rqlited /var/lib/rqlite/data
重要: 即使使用 in-memory 模式,数据也是持久化的——Raft 日志和快照会写入磁盘,启动时会从快照恢复。
3.3.2 WAL 模式
rqlite 默认启用 SQLite 的 WAL(Write-Ahead Logging)模式。WAL 模式有以下优势:
| 特性 | WAL 模式 | 默认日志模式 |
|---|---|---|
| 并发读写 | ✅ 读写不阻塞 | ❌ 读阻塞写 |
| 写入性能 | 更好 | 一般 |
| 崩溃恢复 | 更快 | 一般 |
| 文件数量 | 3 个(db, wal, shm) | 2 个(db, journal) |
3.3.3 SQLite 版本和特性
rqlite 通过 CGO 绑定 SQLite,查看当前版本:
curl -s -G 'localhost:4001/db/query' \
--data-urlencode 'q=SELECT sqlite_version()' | python3 -m json.tool
{
"results": [{
"columns": ["sqlite_version()"],
"types": ["text"],
"values": [["3.45.0"]]
}]
}
支持的 SQLite 高级特性:
| 特性 | 支持情况 |
|---|---|
| FTS5 全文搜索 | ✅ |
| JSON 函数 | ✅ |
| 窗口函数 | ✅ |
| 递归 CTE | ✅ |
| 生成列 | ✅ |
| ATTACH DATABASE | ❌ 不支持 |
3.4 HTTP API 层
3.4.1 API 端点总览
| 端点 | 方法 | 说明 |
|---|---|---|
/status | GET | 查看节点状态 |
/nodes | GET | 查看集群节点列表 |
/db/query | GET/POST | 执行查询(SELECT) |
/db/execute | POST | 执行写入(INSERT/UPDATE/DELETE) |
/db/request | POST | 混合请求(查询+执行) |
/db/backup | GET | 备份数据库 |
/db/load | POST | 加载数据(恢复) |
/join | POST | 加入集群 |
/remove | POST | 移除节点 |
/status/ready | GET | 就绪检查 |
/status/leader | GET | Leader 检查 |
3.4.2 请求和响应格式
所有数据库操作使用 JSON 格式:
查询请求(Query):
curl -G 'localhost:4001/db/query' \
-H 'Content-Type: application/json' \
--data-urlencode 'q=SELECT * FROM users WHERE id > 10' \
--data-urlencode 'level=strong' \
--data-urlencode 'pretty'
执行请求(Execute):
curl -XPOST 'localhost:4001/db/execute' \
-H 'Content-Type: application/json' \
-d '[
["INSERT INTO users (name, email) VALUES (?, ?)", "张三", "[email protected]"],
["INSERT INTO users (name, email) VALUES (?, ?)", "李四", "[email protected]"]
]'
响应格式:
{
"results": [
{
"last_insert_id": 1,
"rows_affected": 1,
"time": 0.000234
},
{
"last_insert_id": 2,
"rows_affected": 1,
"time": 0.000156
}
],
"time": 0.000500
}
3.4.3 读写分离
rqlite 的 HTTP API 层智能地将读写请求路由到不同节点:
┌──────────────┐
│ HTTP API │
└──────┬───────┘
│
┌──────▼───────┐
│ 请求分类 │
└──────┬───────┘
│
┌────────────┼────────────┐
│ │ │
┌─────▼─────┐ ┌───▼───┐ ┌─────▼─────┐
│ Query │ │Execute│ │ Request │
│ (读) │ │ (写) │ │ (混合) │
└─────┬─────┘ └───┬───┘ └─────┬─────┘
│ │ │
┌─────────┼──┐ │ ┌──────┼──────┐
│ level? │ │ │ │ 逐条分类 │
├──────────┤ │ │ └──────┬──────┘
│ strong │ │ │ │
│ → Leader │ │ ┌────▼────┐ ┌────▼────┐
│ weak │ │ │ Leader │ │ Leader │
│ → Leader │ │ └─────────┘ └─────────┘
│ none │ │
│ → 任意 │ │
└──────────┘ │
3.5 数据流完整示例
下面展示一个写操作从客户端到磁盘的完整数据流:
# 1. 客户端发起写请求
curl -XPOST 'localhost:4001/db/execute' \
-H 'Content-Type: application/json' \
-d '[["INSERT INTO orders (product, quantity) VALUES (?, ?)", "笔记本", 5]]'
请求流转过程:
1. HTTP API 层接收请求
2. 请求被分类为 Execute(写操作)
3. 路由到 Leader 节点(如果不是 Leader,返回重定向)
4. 将 SQL 转为 Raft 日志条目
5. Raft 复制日志到 Follower
6. 多数节点确认后,提交日志
7. 应用到 SQLite 存储引擎
8. 返回操作结果给客户端
3.6 内存与磁盘使用
3.6.1 文件结构
数据目录下的文件结构:
/var/lib/rqlite/data/
├── db.sqlite # SQLite 数据库文件
├── db.sqlite-wal # WAL 文件(Write-Ahead Log)
├── db.sqlite-shm # 共享内存文件
├── raft/ # Raft 日志目录
│ ├── logs.db # Raft 日志数据库
│ ├── stable.db # Raft 稳定存储
│ └── snapshots/ # 快照目录
│ └── 1-5-1234567890/
│ └── db.sqlite # 快照中的数据库文件
└── raft-peers.json # 节点配置
3.6.2 内存消耗估算
| 数据量 | in-memory 模式 | on-disk 模式 |
|---|---|---|
| 100 MB | ~300-500 MB | ~100-150 MB |
| 1 GB | ~2-3 GB | ~200-400 MB |
| 10 GB | 不推荐 | ~500 MB - 1 GB |
建议: 数据量超过 1 GB 时使用
-on-disk模式。
3.7 本章小结
| 要点 | 内容 |
|---|---|
| 三层架构 | HTTP API → Raft 共识 → SQLite 存储 |
| Raft 实现 | 基于 hashicorp/raft,支持 Leader 选举和日志复制 |
| 快照机制 | 定期压缩日志,支持增量传输 |
| 存储模式 | in-memory(默认)和 on-disk |
| API 设计 | RESTful 风格,JSON 格式,支持读写分离 |
| 文件结构 | SQLite 文件 + Raft 日志 + 快照目录 |
上一章:第 2 章:安装与集群搭建 下一章:第 4 章:基础操作