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

dqlite 分布式 SQLite 教程 / 第 2 章:安装与编译

第 2 章:安装与编译

本章介绍如何在 Linux 上从源码编译 dqlite、使用包管理器安装、Docker 快速部署,以及 Go 语言绑定的配置方法。


2.1 环境准备

dqlite 仅支持 Linux 系统,以下是最低环境要求:

项目最低版本说明
操作系统Linux(内核 3.10+)依赖 epoll,不支持 macOS/Windows
GCC9.0+需要 C11 支持
CMake3.16+或 autotools(autoconf/automake)
autoconf2.69+源码编译需要
automake1.14+源码编译需要
libtool2.4+源码编译需要
pkg-config0.29+库查找
zlib1.2+压缩支持

2.1.1 Ubuntu / Debian 安装依赖

sudo apt update
sudo apt install -y \
    build-essential \
    autoconf automake libtool \
    pkg-config \
    libuv1-dev \
    zlib1g-dev \
    liblz4-dev \
    libsqlite3-dev

2.1.2 CentOS / RHEL / Fedora 安装依赖

# Fedora
sudo dnf install -y \
    gcc make autoconf automake libtool \
    pkg-config \
    libuv-devel \
    zlib-devel \
    lz4-devel \
    sqlite-devel

# CentOS / RHEL (需要 EPEL)
sudo yum install -y epel-release
sudo yum install -y \
    gcc make autoconf automake libtool \
    pkg-config \
    libuv-devel \
    zlib-devel \
    lz4-devel \
    sqlite-devel

2.1.3 Alpine Linux 安装依赖

apk add \
    build-base \
    autoconf automake libtool \
    pkgconfig \
    libuv-dev \
    zlib-dev \
    lz4-dev \
    sqlite-dev

2.2 从源码编译

2.2.1 编译 libuv

dqlite 依赖 libuv 作为异步 I/O 库。如果你的系统没有提供 libuv-dev 包,需要手动编译:

# 下载 libuv
wget https://dist.libuv.org/dist/v1.48.0/libuv-v1.48.0.tar.gz
tar xzf libuv-v1.48.0.tar.gz
cd libuv-v1.48.0

# 编译安装
sh autogen.sh
./configure --prefix=/usr/local
make -j$(nproc)
sudo make install
sudo ldconfig

2.2.2 编译 dqlite

# 克隆源码
git clone https://github.com/canonical/dqlite.git
cd dqlite

# 检出最新稳定版
git checkout v1.16.6

# 编译
autoreconf -i
./configure
make -j$(nproc)

# 安装
sudo make install
sudo ldconfig

2.2.3 验证安装

# 检查库文件
ls -la /usr/local/lib/libdqlite*

# �期输出:
# /usr/local/lib/libdqlite.so -> libdqlite.so.0.0.1
# /usr/local/lib/libdqlite.so.0 -> libdqlite.so.0.0.1
# /usr/local/lib/libdqlite.so.0.0.1

# 检查头文件
ls /usr/local/include/dqlite.h

# 检查 pkg-config
pkg-config --modversion dqlite
# 预期输出:1.16.6(或类似版本号)

2.2.4 编译选项说明

选项默认值说明
--enable-debugoff启用调试模式(-g -O0)
--enable-staticoff编译静态库
--enable-build-rafton内嵌编译 Raft 库
--with-lz4auto启用 LZ4 压缩
--prefix/usr/local安装路径

启用调试模式编译:

./configure --enable-debug
make -j$(nproc)
sudo make install

2.3 使用系统包管理器

某些 Linux 发行版已经提供了 dqlite 包:

2.3.1 Ubuntu

# Ubuntu 22.04+ 提供了 dqlite 库包
sudo apt install libdqlite-dev

# 验证
dpkg -l | grep dqlite

注意: 发行版仓库中的版本可能较旧。如需最新版本,建议从源码编译。

2.3.2 Snap

# Canonical 的 LXD 项目打包了 dqlite
sudo snap install lxd

2.3.3 版本对比

安装方式版本更新频率适用场景
源码编译最新手动生产环境、开发
Ubuntu APT较旧跟随发行版快速体验
Snap较新自动与 LXD 配合

2.4 Docker 快速部署

使用 Docker 是快速体验 dqlite 的最佳方式。

2.4.1 单节点演示

# 使用官方 dqlite-demo 镜像
docker run -d --name dqlite-demo \
    -p 9001:9001 \
    -v dqlite-data:/data \
    -e DQLITE_NODE_ID=1 \
    -e DQLITE_BIND_ADDRESS=0.0.0.0:9001 \
    -e DQLITE_DATA_DIR=/data \
    dqlite/dqlite-demo:latest

注意: 截至本文编写时,dqlite 官方未提供统一的演示 Docker 镜像。以下方式是构建自定义镜像。

2.4.2 自定义 Dockerfile

# Dockerfile.dqlite
FROM ubuntu:24.04

RUN apt-get update && apt-get install -y \
    build-essential \
    autoconf automake libtool \
    pkg-config \
    libuv1-dev \
    zlib1g-dev \
    liblz4-dev \
    libsqlite3-dev \
    wget \
    && rm -rf /var/lib/apt/lists/*

# 编译 dqlite
WORKDIR /build
RUN wget -q https://github.com/canonical/dqlite/archive/refs/tags/v1.16.6.tar.gz \
    && tar xzf v1.16.6.tar.gz \
    && cd dqlite-1.16.6 \
    && autoreconf -i \
    && ./configure --prefix=/usr \
    && make -j$(nproc) \
    && make install \
    && ldconfig

# 编译 demo 程序
COPY demo.c /build/demo.c
RUN gcc -o /usr/local/bin/dqlite-demo /build/demo.c -ldqlite -lpthread

# 数据目录
RUN mkdir -p /data
VOLUME /data

EXPOSE 9001

CMD ["dqlite-demo"]

2.4.3 构建并运行

# 构建镜像
docker build -t my-dqlite:latest -f Dockerfile.dqlite .

# 运行
docker run -d --name dqlite-node1 \
    -p 9001:9001 \
    -v dqlite-node1-data:/data \
    my-dqlite:latest

2.5 C 语言库集成

2.5.1 头文件和库文件

编译安装 dqlite 后,系统中会有以下文件:

文件路径说明
头文件/usr/local/include/dqlite.hC API 声明
共享库/usr/local/lib/libdqlite.so动态链接库
pkg-config/usr/local/lib/pkgconfig/dqlite.pc编译配置

2.5.2 编译链接

使用 pkg-config:

gcc -o myapp myapp.c $(pkg-config --cflags --libs dqlite) -lpthread

手动指定路径:

gcc -o myapp myapp.c \
    -I/usr/local/include \
    -L/usr/local/lib \
    -ldqlite -lpthread \
    -Wl,-rpath,/usr/local/lib

2.5.3 最小程序示例

/* minimal_dqlite.c */
#include <dqlite.h>
#include <stdio.h>
#include <stdlib.h>

int main() {
    dqlite_node *node;
    int rc;

    /* 1. 创建节点 */
    rc = dqlite_node_create(
        1,                              /* 节点 ID */
        "/tmp/dqlite-minimal-data",     /* 数据目录 */
        "127.0.0.1:9001",              /* 绑定地址 */
        &node                           /* 输出节点指针 */
    );
    if (rc != 0) {
        fprintf(stderr, "Create failed: %d\n", rc);
        return EXIT_FAILURE;
    }

    /* 2. 启动节点 */
    rc = dqlite_node_start(node);
    if (rc != 0) {
        fprintf(stderr, "Start failed: %s\n", dqlite_node_errmsg(node));
        dqlite_node_destroy(node);
        return EXIT_FAILURE;
    }

    printf("dqlite node started on 127.0.0.1:9001\n");
    printf("Data dir: /tmp/dqlite-minimal-data\n");

    /* 3. 等待(实际应用中在此处理业务逻辑) */
    printf("Press Enter to stop...\n");
    getchar();

    /* 4. 停止并清理 */
    dqlite_node_stop(node);
    dqlite_node_destroy(node);

    return EXIT_SUCCESS;
}

编译和运行:

gcc -Wall -o minimal_dqlite minimal_dqlite.c $(pkg-config --cflags --libs dqlite) -lpthread
./minimal_dqlite

2.5.4 CMake 集成

在你的 CMake 项目中使用 dqlite:

# CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(my_dqlite_app C)

set(CMAKE_C_STANDARD 11)

# 查找 dqlite
find_package(PkgConfig REQUIRED)
pkg_check_modules(DQLITE REQUIRED dqlite)

add_executable(myapp src/main.c)
target_include_directories(myapp PRIVATE ${DQLITE_INCLUDE_DIRS})
target_link_libraries(myapp ${DQLITE_LIBRARIES} pthread)

2.5.5 Makefile 示例

CC = gcc
CFLAGS = -Wall -Wextra -O2 $(shell pkg-config --cflags dqlite)
LDFLAGS = $(shell pkg-config --libs dqlite) -lpthread

SRCS = $(wildcard src/*.c)
OBJS = $(SRCS:.c=.o)
TARGET = myapp

all: $(TARGET)

$(TARGET): $(OBJS)
	$(CC) -o $@ $^ $(LDFLAGS)

%.o: %.c
	$(CC) $(CFLAGS) -c $< -o $@

clean:
	rm -f $(OBJS) $(TARGET)

.PHONY: all clean

2.6 Go 语言绑定

go-dqlite 是 Canonical 维护的 Go 语言绑定,LXD 和 MicroK8s 都使用它。

2.6.1 安装 go-dqlite

# 前提:已安装 Go 1.21+ 和编译好的 libdqlite
go get github.com/canonical/go-dqlite/v2

2.6.2 Go 绑定工作原理

go-dqlite 提供了 database/sql 驱动,可以直接使用标准 Go SQL 接口:

┌─────────────────────────────────┐
│         Go Application          │
│  ┌───────────────────────────┐  │
│  │  database/sql 接口        │  │
│  └─────────┬─────────────────┘  │
│            │                     │
│  ┌─────────▼─────────────────┐  │
│  │  go-dqlite driver         │  │
│  │  ┌─────────────────────┐  │  │
│  │  │  CGO → libdqlite.so │  │  │
│  │  └─────────────────────┘  │  │
│  └───────────────────────────┘  │
└─────────────────────────────────┘

2.6.3 Go 示例程序

package main

import (
    "context"
    "database/sql"
    "fmt"
    "log"
    "os"
    "time"

    dqlite "github.com/canonical/go-dqlite/v2"
    "github.com/canonical/go-dqlite/v2/driver"
)

func main() {
    dir, err := os.MkdirTemp("", "dqlite-example")
    if err != nil {
        log.Fatal(err)
    }
    defer os.RemoveAll(dir)

    // 创建节点
    node, err := dqlite.New(1, "127.0.0.1:9001", dir, nil)
    if err != nil {
        log.Fatal(err)
    }
    defer node.Close()

    // 创建数据库驱动
    drv, err := driver.New(map[uint64]string{1: "127.0.0.1:9001"},
        driver.WithLogFunc(func(level dqlite.LogLevel, msg string, args ...interface{}) {
            fmt.Printf("[%s] %s\n", level, fmt.Sprintf(msg, args...))
        }),
    )
    if err != nil {
        log.Fatal(err)
    }
    defer drv.Close()

    // 打开数据库
    db := sql.OpenDB(drv)
    defer db.Close()

    // 等待连接就绪
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()
    if err := db.PingContext(ctx); err != nil {
        log.Fatal(err)
    }

    // 创建表
    _, err = db.Exec(`CREATE TABLE IF NOT EXISTS users (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        name TEXT NOT NULL,
        email TEXT UNIQUE
    )`)
    if err != nil {
        log.Fatal(err)
    }

    // 插入数据
    result, err := db.Exec("INSERT INTO users (name, email) VALUES (?, ?)",
        "张三", "[email protected]")
    if err != nil {
        log.Fatal(err)
    }
    id, _ := result.LastInsertId()
    fmt.Printf("Inserted user id: %d\n", id)

    // 查询
    var name, email string
    err = db.QueryRow("SELECT name, email FROM users WHERE id = ?", id).Scan(&name, &email)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("User: %s <%s>\n", name, email)
}

2.6.4 Go 项目结构建议

my-dqlite-app/
├── go.mod
├── go.sum
├── main.go
├── node/
│   ├── node.go        # dqlite 节点管理
│   └── config.go      # 配置加载
├── store/
│   ├── db.go          # 数据库操作封装
│   └── migrate.go     # Schema 迁移
└── Makefile

2.6.5 CGO 依赖注意事项

go-dqlite 使用 CGO 调用 libdqlite.so,需要注意以下事项:

事项说明
CGO_ENABLED=1Go 编译时必须启用 CGO
libdqlite.so 在链接路径中通过 LD_LIBRARY_PATHldconfig
编译机器需要 libdqlite-dev交叉编译需要对应架构的库
静态编译需要 libdqlite 的 .a 文件

静态编译示例:

CGO_ENABLED=1 go build \
    -ldflags '-linkmode external -extldflags "-static -ldqlite -lraft -lsqlite3 -luv -llz4 -lpthread"' \
    -o myapp .

2.7 常见问题

2.7.1 编译错误

错误原因解决方案
dqlite.h: No such file头文件未找到安装 libdqlite-dev 或指定 -I 路径
libdqlite.so: cannot open运行时找不到库执行 sudo ldconfig 或设置 LD_LIBRARY_PATH
libuv not foundlibuv 未安装sudo apt install libuv1-dev
undefined reference to sqlite3_*SQLite 库未链接添加 -lsqlite3
configure: error: liblz4 not foundLZ4 库缺失sudo apt install liblz4-dev

2.7.2 运行时错误

错误原因解决方案
bind: address already in use端口被占用更换端口或关闭占用进程
permission denied数据目录权限不足检查目录权限
no such file or directory数据目录不存在先创建数据目录

2.7.3 数据目录准备

# 创建数据目录
mkdir -p /var/lib/dqlite/node1

# 设置权限(建议使用专用用户)
sudo useradd -r -s /bin/false dqlite
sudo chown -R dqlite:dqlite /var/lib/dqlite

2.8 开发环境配置

2.8.1 VS Code 配置

创建 .vscode/c_cpp_properties.json

{
    "configurations": [
        {
            "name": "Linux",
            "includePath": [
                "${workspaceFolder}/**",
                "/usr/local/include"
            ],
            "defines": [],
            "compilerPath": "/usr/bin/gcc",
            "cStandard": "c11",
            "intelliSenseMode": "linux-gcc-x64",
            "browse": {
                "path": ["/usr/local/include"],
                "limitSymbolsToIncludedHeaders": true
            }
        }
    ],
    "version": 4
}

2.8.2 使用 Docker 作为开发环境

# docker-compose.dev.yml
version: '3.8'
services:
  dev:
    build:
      context: .
      dockerfile: Dockerfile.dev
    volumes:
      - .:/workspace
      - dqlite-dev-data:/data
    working_dir: /workspace
    stdin_open: true
    tty: true
    cap_add:
      - SYS_PTRACE
    security_opt:
      - seccomp:unconfined

volumes:
  dqlite-dev-data:
# Dockerfile.dev
FROM ubuntu:24.04

ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update && apt-get install -y \
    build-essential \
    autoconf automake libtool \
    pkg-config \
    libuv1-dev \
    zlib1g-dev \
    liblz4-dev \
    libsqlite3-dev \
    gdb valgrind \
    vim git curl wget \
    golang-go \
    && rm -rf /var/lib/apt/lists/*

# 编译安装 dqlite
WORKDIR /tmp
RUN git clone --depth 1 --branch v1.16.6 https://github.com/canonical/dqlite.git \
    && cd dqlite \
    && autoreconf -i \
    && ./configure --enable-debug --prefix=/usr \
    && make -j$(nproc) \
    && make install \
    && ldconfig \
    && rm -rf /tmp/dqlite

WORKDIR /workspace
CMD ["/bin/bash"]

本章小结

要点说明
系统要求Linux only,需要 GCC、libuv、SQLite
源码编译autoreconf -i && ./configure && make
Docker最快的上手方式
C 集成链接 libdqlite.so,使用 pkg-config
Go 绑定go-dqlite 通过 CGO 调用 libdqlite
数据目录需要预先创建并设置正确权限

下一章

第 3 章:架构深度解析 — 深入了解 dqlite 的内部架构,包括 Raft 共识、日志复制、快照机制和成员变更。