强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

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 集群中:

  1. 所有写操作 必须经过 Leader 节点
  2. Leader 将写操作以日志(Log Entry)形式广播给所有 Follower
  3. 多数节点(Quorum)确认收到日志后,该写操作被提交(Commit)
  4. 提交后的日志被应用到本地 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 快速上手。