dqlite 分布式 SQLite 教程 / 第 1 章:dqlite 概述与适用场景
第 1 章:dqlite 概述与适用场景
本章介绍 dqlite 的核心理念、技术背景、适用场景,并与 rqlite 进行详细对比,帮助你判断 dqlite 是否适合你的项目。
1.1 什么是 dqlite?
dqlite(发音为 “dee-queue-lite”,即 distributed SQLite)是一个用 C 语言编写的开源库,它将 SQLite 扩展为一个分布式、容错的关系型数据库。
dqlite 的核心设计理念是:
| 设计原则 | 说明 |
|---|---|
| 嵌入式优先 | 作为 C 库链接到你的应用中,不依赖外部服务进程 |
| 零配置 | 不需要独立的数据库服务器,不需要复杂配置 |
| SQLite 兼容 | 支持标准 SQL 和 SQLite 的大部分特性 |
| 高可用 | 基于 Raft 共识协议实现多节点数据复制 |
| 轻量级 | 最小化资源占用,适合边缘设备和容器 |
┌─────────────────────────────────────┐
│ Your Application │
│ ┌───────────┐ ┌──────────────┐ │
│ │ App Logic │──▶│ libdqlite.so │ │
│ └───────────┘ │ ┌────────┐ │ │
│ │ │SQLite │ │ │
│ │ │Engine │ │ │
│ │ ├────────┤ │ │
│ │ │ Raft │ │ │
│ │ │Module │ │ │
│ │ └────────┘ │ │
│ └──────────────┘ │
│ │ │
│ ┌─────┴─────┐ │
│ │ Network │ │
│ │ Transport │ │
│ └─────┬─────┘ │
└──────────────────────┬─┴────────────┘
│
┌──────────┼──────────┐
▼ ▼ ▼
┌──────┐ ┌──────┐ ┌──────┐
│Node 2│ │Node 3│ │Node N│
└──────┘ └──────┘ └──────┘
1.1.1 项目背景
dqlite 由 Canonical 工程师发起,最初是为了解决 LXD(Linux 容器管理器)的高可用需求。LXD 需要一个轻量的、可嵌入的分布式存储来保存集群状态,但不想引入 etcd 或 Consul 这类重量级组件。
“我们需要一个像 SQLite 一样简单,但又能跨多节点复制数据的数据库。” — Stéphane Graber,LXD 项目负责人
1.1.2 技术特点一览
| 特性 | 描述 |
|---|---|
| 实现语言 | C(核心)+ Go 绑定 |
| 共识算法 | Raft(基于 C-raft 库) |
| 存储引擎 | SQLite |
| 通信协议 | 自定义二进制协议(基于 MessagePack) |
| 最大节点数 | 无硬限制(推荐 3-7 个) |
| 数据一致性 | 强一致性(线性一致性读写) |
| 支持平台 | Linux only(依赖 epoll) |
| 许可证 | LGPLv3 |
1.2 Raft 共识协议简介
dqlite 使用 Raft 共识协议来保证多节点间数据的一致性。理解 Raft 的基本概念对使用 dqlite 集群至关重要。
1.2.1 Raft 核心概念
Raft 是一种分布式共识算法,其设计目标是易于理解。它将共识问题分解为三个子问题:
| 子问题 | 说明 |
|---|---|
| Leader 选举(Leader Election) | 集群中选出一个 Leader 节点负责协调写入 |
| 日志复制(Log Replication) | Leader 将写操作日志复制到所有 Follower |
| 安全性(Safety) | 保证所有节点最终执行相同的日志序列 |
┌──────────┐ 日志复制 ┌──────────┐
│ Leader │ ───────────────▶ │ Follower │
│ │ ───────────────▶ │ │
└────┬─────┘ └──────────┘
│
│ 写请求 ┌──────────┐
│ │ Follower │
└─────────────────────── ▶ │ │
└──────────┘
1.2.2 Raft 在 dqlite 中的工作方式
在 dqlite 集群中:
- 所有写操作 必须经过 Leader 节点
- Leader 将写操作以日志(Log Entry)形式广播给所有 Follower
- 当多数节点(Quorum)确认收到日志后,该写操作被提交(Commit)
- 提交后的日志被应用到本地 SQLite 数据库
Client ──▶ Leader
│
├──▶ Follower 1 (确认 ✓)
├──▶ Follower 2 (确认 ✓)
│
[多数确认]
│
▼
提交并应用
注意: 读操作默认也需要经过 Leader 以保证线性一致性。dqlite 支持通过配置允许 Follower 读取(可能返回稍旧的数据)。
1.3 dqlite 与 rqlite 详细对比
如果你同时在评估 dqlite 和 rqlite,以下对比表可以帮助你做出选择:
| 维度 | dqlite | rqlite |
|---|---|---|
| 实现语言 | C | Go |
| 部署方式 | 嵌入应用(库) | 独立服务进程 |
| 通信协议 | 自定义二进制协议 | HTTP REST API |
| 查询接口 | C API / Go 绑定 | HTTP 请求(curl 可调用) |
| 开发复杂度 | 较高(需管理生命周期) | 较低(HTTP 调用即可) |
| 运行时开销 | 低(进程内调用) | 中(HTTP + JSON 序列化) |
| 写延迟 | ~0.1ms | ~1-5ms |
| 读延迟 | ~0.01ms | ~0.5-2ms |
| 客户端生态 | C / Go 语言绑定 | 任意语言(HTTP) |
| 运维复杂度 | 中等 | 低 |
| 适用场景 | 嵌入式、容器运行时、系统软件 | 微服务、HTTP 后端、Web 应用 |
| 主要用户 | LXD、MicroK8s | 各类中小型项目 |
| GitHub Stars | ~2.5k | ~13k |
| 成熟度 | 生产级(Canonical 维护) | 生产级(活跃社区) |
1.3.1 架构差异
rqlite 架构(客户端-服务器):
┌──────────┐ HTTP ┌────────────┐ ┌──────────┐
│ Client │ ──────────────▶│ rqlite │────▶│ SQLite │
│ (curl等) │◀──────────────│ Server │ └──────────┘
└──────────┘ JSON 响应 │ (Go进程) │
│ + Raft │
└─────┬──────┘
│
┌─────┴──────┐
│ Cluster │
│ (Raft) │
└────────────┘
dqlite 架构(嵌入式库):
┌────────────────────────────────┐
│ Your Application │
│ ┌──────────────┐ │
│ │ App Logic │ │
│ └──────┬───────┘ │
│ │ C API / Go 绑定 │
│ ┌──────▼───────┐ │
│ │ libdqlite │ │
│ │ ┌────────┐ │ │
│ │ │SQLite │ │ │
│ │ │+ Raft │ │ │
│ │ └────────┘ │ │
│ └──────┬───────┘ │
└─────────┼──────────────────────┘
│ 二进制协议
┌─────┼─────┐
▼ ▼ ▼
Node2 Node3 NodeN
1.3.2 选择建议
| 场景 | 推荐 | 理由 |
|---|---|---|
| 已有 Go 微服务,需要分布式存储 | rqlite | HTTP API 更易集成 |
| C/C++ 系统软件,需要嵌入式数据库 | dqlite | C 库直接嵌入 |
| 容器运行时 / Kubernetes 相关 | dqlite | Canonical 生态支持好 |
| 快速原型开发 | rqlite | curl 即可操作 |
| 对延迟敏感的嵌入式场景 | dqlite | 进程内调用延迟极低 |
| 需要多语言客户端 | rqlite | HTTP 协议语言无关 |
| 已在使用 LXD / MicroK8s | dqlite | 一致的技术栈 |
1.4 dqlite 适用场景
1.4.1 适用场景
| 场景 | 说明 | 案例 |
|---|---|---|
| 容器/虚拟机管理器 | 存储集群元数据和配置 | LXD |
| Kubernetes 数据存储 | 替代 etcd 的轻量方案 | MicroK8s |
| 边缘计算 | 在资源受限设备上运行分布式数据库 | IoT 网关 |
| 嵌入式系统 | 需要关系型数据库 + 高可用的嵌入式应用 | 工业控制 |
| 配置管理 | 小规模分布式配置存储 | 微服务配置中心 |
| 本地优先应用 | 离线可用、自动同步的桌面/移动应用 | 协同编辑工具 |
1.4.2 不适用场景
| 场景 | 说明 | 替代方案 |
|---|---|---|
| 高吞吐写入 | SQLite 写入受限于单线程 WAL | PostgreSQL、TiKV |
| 大规模数据集 | 推荐数据量 < 10GB | PostgreSQL、MySQL |
| 复杂查询优化 | SQLite 查询优化器不如大型数据库 | PostgreSQL |
| 跨平台需求 | 仅支持 Linux | rqlite、CockroachDB |
| 多数据中心 | 不适合高延迟的跨地域复制 | CockroachDB、YugabyteDB |
| 高并发读写 | 共享缓存模式下并发有限 | PostgreSQL、MySQL |
1.4.3 容量参考
| 指标 | 推荐上限 | 说明 |
|---|---|---|
| 节点数 | 3-7 | Raft 多数派机制,奇数节点 |
| 数据库大小 | < 10GB | SQLite WAL 模式性能限制 |
| 并发连接 | ~100 | 共享缓存模式限制 |
| 写 QPS | ~1000-5000 | 取决于硬件和同步策略 |
| 读 QPS | ~10000-50000 | 本地读取,可 Follower 分担 |
1.5 dqlite 生态系统
dqlite 不仅仅是一个库,它有一套完整的生态系统:
| 组件 | 说明 |
|---|---|
| libdqlite | 核心 C 库,实现 Raft 共识和 SQLite 集成 |
| go-dqlite | Go 语言绑定,LXD 和 MicroK8s 使用 |
| C-raft | C 语言实现的 Raft 协议库(内嵌在 dqlite 中) |
| dqlite-demo | 官方演示程序 |
| canonical/dqlite | GitHub 主仓库 |
1.5.1 版本历史里程碑
| 版本 | 时间 | 重要变更 |
|---|---|---|
| v1.0 | 2019 | 首个稳定版本,LXD 正式采用 |
| v1.6 | 2021 | 引入共享缓存模式 |
| v1.9 | 2022 | 改进快照机制 |
| v1.12 | 2023 | 性能优化,日志压缩改进 |
| v1.15 | 2024 | TLS 支持增强 |
| v1.16 | 2025 | 成员变更协议改进 |
1.6 快速感受:30 秒体验 dqlite
在深入了解之前,先感受一下 dqlite 的使用方式:
使用 Go 绑定启动单节点
package main
import (
"context"
"database/sql"
"fmt"
"log"
_ "github.com/canonical/go-dqlite/driver"
)
func main() {
// 创建 dqlite 节点
dir := "/tmp/dqlite-data"
node, err := dqlite.New(1, "127.0.0.1:9001", dir, nil)
if err != nil {
log.Fatal(err)
}
defer node.Close()
// 打开数据库连接
db, err := sql.Open("dqlite", "test.db")
if err != nil {
log.Fatal(err)
}
defer db.Close()
// 创建表
_, err = db.Exec("CREATE TABLE IF NOT EXISTS demo (id INTEGER PRIMARY KEY, name TEXT)")
if err != nil {
log.Fatal(err)
}
// 插入数据
_, err = db.Exec("INSERT INTO demo VALUES(1, 'hello dqlite')")
if err != nil {
log.Fatal(err)
}
// 查询
var name string
err = db.QueryRow("SELECT name FROM demo WHERE id = 1").Scan(&name)
if err != nil {
log.Fatal(err)
}
fmt.Println("Result:", name) // Output: Result: hello dqlite
}
使用 C API 启动单节点
#include <dqlite.h>
#include <stdio.h>
#include <unistd.h>
int main() {
dqlite_node *node;
int rc;
// 创建节点
rc = dqlite_node_create(1, "/tmp/dqlite-data", "127.0.0.1:9001", &node);
if (rc != 0) {
fprintf(stderr, "Error: %s\n", dqlite_node_errmsg(node));
return 1;
}
// 设置日志级别
dqlite_node_set_bind_address(node, "127.0.0.1:9001");
// 启动节点
rc = dqlite_node_start(node);
if (rc != 0) {
fprintf(stderr, "Start error: %s\n", dqlite_node_errmsg(node));
return 1;
}
printf("dqlite node running. Press Enter to stop.\n");
getchar();
// 停止并释放
dqlite_node_stop(node);
dqlite_node_destroy(node);
return 0;
}
编译并运行:
gcc -o dqlite-demo dqlite-demo.c -ldqlite -lsqlite3 -lraft -lpthread
./dqlite-demo
1.7 核心术语表
在后续章节中会频繁出现以下术语:
| 术语 | 英文 | 说明 |
|---|---|---|
| 节点 | Node | 集群中的一个 dqlite 实例 |
| Leader | Leader | Raft 集群中负责协调写入的节点 |
| Follower | Follower | 跟随 Leader 复制日志的节点 |
| 候选人 | Candidate | 发起选举的节点(临时状态) |
| 日志条目 | Log Entry | 一条写操作记录 |
| 提交 | Commit | 日志条目被多数节点确认后生效 |
| 快照 | Snapshot | 数据库在某一时刻的完整状态 |
| 任期 | Term | Leader 的任期编号 |
| 心跳 | Heartbeat | Leader 周期性发送的心跳信号 |
| 法定人数 | Quorum | 多数节点(N/2 + 1) |
| 线性一致性 | Linearizability | 最强的一致性保证 |
本章小结
| 要点 | 说明 |
|---|---|
| dqlite 是什么 | C 语言实现的嵌入式分布式 SQLite |
| 核心机制 | Raft 共识 + SQLite 存储 |
| 最大优势 | 极低延迟、零外部依赖、可嵌入 |
| 最大限制 | 仅支持 Linux、需要自行管理生命周期 |
| 与 rqlite 的区别 | 嵌入式库 vs 独立服务、二进制协议 vs HTTP |
| 适用场景 | 嵌入式系统、容器管理、边缘计算 |
下一章
→ 第 2 章:安装与编译 — 学习如何在 Linux 上编译和安装 dqlite,以及使用 Docker 快速上手。