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

dqlite 分布式 SQLite 教程 / 第 10 章:生产最佳实践

第 10 章:生产最佳实践

本章总结 dqlite 在生产环境中的最佳实践,包括技术选型决策、容量规划、监控告警、运维 SOP 和常见陷阱。


10.1 何时选择 dqlite

10.1.1 决策树

需要分布式数据库?
    │
    ├── 否 → 直接用 SQLite
    │
    └── 是 → 数据量多大?
              │
              ├── > 10GB → 考虑 PostgreSQL / CockroachDB
              │
              └── ≤ 10GB → 需要 HTTP API?
                            │
                            ├── 是 → 考虑 rqlite
                            │
                            └── 否 → 需要嵌入式?
                                      │
                                      ├── 是 → 平台是 Linux?
                                      │        │
                                      │        ├── 是 → ✅ 选择 dqlite
                                      │        │
                                      │        └── 否 → 考虑 rqlite 或 etcd
                                      │
                                      └── 否 → 考虑 rqlite / Consul / etcd

10.1.2 dqlite 适用场景详细分析

场景评分说明
容器管理器/运行时⭐⭐⭐⭐⭐LXD 验证的最佳场景
Kubernetes 数据存储⭐⭐⭐⭐MicroK8s 使用,适合中小规模
边缘计算/IoT⭐⭐⭐⭐轻量级,资源占用小
嵌入式 Linux 设备⭐⭐⭐⭐无外部依赖
配置管理⭐⭐⭐可用,但 etcd 可能更成熟
Web 应用后端⭐⭐rqlite 更合适(HTTP API)
高吞吐数据管道不适合,考虑 Kafka + 数据库
多数据中心不适合,考虑 CockroachDB

10.1.3 与替代方案对比

维度dqliteetcdConsulrqlite
存储模型关系型 (SQL)KVKV关系型 (SQL)
查询能力完整 SQL有限完整 SQL
嵌入式
依赖libuv
数据量上限~10GB~8GB~10GB~10GB
成熟度最高
复杂度

10.2 容量规划

10.2.1 节点规格建议

规模节点数CPU内存存储网络
开发测试11 核512MB1GB SSD任意
小型生产32 核1GB10GB SSD千兆
中型生产34 核4GB50GB SSD千兆
大型生产58 核8GB100GB SSD万兆

10.2.2 数据容量评估

存储空间计算:

数据库大小 = 原始数据 + 索引 + WAL 缓冲 + Raft 日志

示例计算:
  原始数据:        1GB
  索引 (约 30%):   0.3GB
  WAL 缓冲:        0.1GB
  Raft 日志 (2x):  0.2GB (快照后保留)
  安全余量 (20%):  0.32GB
  ─────────────────────
  总计:            ~2GB

磁盘建议: 原始数据 × 3 倍

10.2.3 性能容量计算

写入容量(3 节点集群):
  单条写 QPS ≈ 1000-2000 (取决于硬件和网络)
  批量写 QPS ≈ 5000-20000 (100 条/事务)

读取容量:
  单节点读 QPS ≈ 10000-50000
  3 节点分散读 ≈ 30000-150000

连接数:
  推荐 10-50 个连接(SQLite 写入串行)

并发事务:
  写事务: 串行(1 个并发)
  读事务: 并发(受限于连接数)

10.2.4 容量规划表

指标公式示例
所需磁盘数据量 × 310GB 数据 → 30GB 磁盘
所需内存max(1GB, 数据量 × 0.1 + 256MB)10GB 数据 → 1.25GB
所需 CPU基础 1 核 + 每 1000 QPS 加 1 核5000 QPS → 6 核
网络带宽写入 QPS × 平均事务大小 × 节点数1000 × 1KB × 3 = 3MB/s

10.3 监控告警

10.3.1 关键监控指标

类别指标阈值告警级别
可用性节点存活数< QuorumP0 (严重)
可用性Leader 是否存在无 Leader > 5sP0
性能写延迟 P99> 10msP1 (警告)
性能读延迟 P99> 5msP1
性能写 QPS> 容量 80%P2 (关注)
存储磁盘使用率> 80%P1
存储数据库大小> 容量 70%P1
Raft日志滞后> 1000 条P1
Raft快照耗时> 30sP2
网络节点间延迟> 10msP1

10.3.2 Prometheus 监控配置

# prometheus/alert-rules.yaml
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: dqlite-alerts
spec:
  groups:
    - name: dqlite
      rules:
        # 节点存活检查
        - alert: DqliteNodeDown
          expr: up{job="dqlite"} == 0
          for: 1m
          labels:
            severity: critical
          annotations:
            summary: "dqlite 节点 {{ $labels.instance }} 不可用"
            description: "节点已离线超过 1 分钟"

        # Leader 缺失
        - alert: DqliteNoLeader
          expr: dqlite_raft_leader_id == 0
          for: 5s
          labels:
            severity: critical
          annotations:
            summary: "dqlite 集群无 Leader"

        # 写延迟过高
        - alert: DqliteHighWriteLatency
          expr: histogram_quantile(0.99, dqlite_write_latency_seconds_bucket) > 0.01
          for: 5m
          labels:
            severity: warning
          annotations:
            summary: "dqlite 写延迟 P99 > 10ms"

        # 磁盘空间
        - alert: DqliteDiskSpaceHigh
          expr: (dqlite_disk_used_bytes / dqlite_disk_total_bytes) > 0.8
          for: 5m
          labels:
            severity: warning
          annotations:
            summary: "dqlite 磁盘使用率 > 80%"

        # Raft 日志滞后
        - alert: DqliteRaftLogLag
          expr: dqlite_raft_log_entries - dqlite_raft_commit_index > 1000
          for: 5m
          labels:
            severity: warning
          annotations:
            summary: "Raft 日志滞后 > 1000 条"

10.3.3 Grafana Dashboard

关键面板建议:

┌─────────────────────────────────────────────────────────────┐
│                    dqlite Dashboard                          │
├─────────────┬─────────────┬─────────────┬──────────────────┤
│ 节点状态     │ Leader 状态  │ 集群大小    │ 当前任期          │
│  ● 3/3     │  ● Node 1   │  ● 3       │  ● 42           │
├─────────────┴─────────────┴─────────────┴──────────────────┤
│ 写延迟 (P50/P95/P99)                                        │
│ ████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░  2.1ms / 4.5ms / 8.2ms│
├─────────────────────────────────────────────────────────────┤
│ QPS (写入/读取)                                              │
│ ▁▂▃▄▅▆▇█▇▆▅▄▃▂▁▂▃▄▅▆▇  写: 1.2K  读: 8.5K                 │
├──────────────┬──────────────┬──────────────────────────────┤
│ 磁盘使用率    │ 数据库大小    │ Raft 日志条目数               │
│ ▓▓▓▓▓░░░ 45% │  2.3GB       │  1,234                       │
├──────────────┴──────────────┴──────────────────────────────┤
│ 快照状态                                                    │
│ 上次快照: 2 分钟前  │  耗时: 1.2s  │  日志已清理: 1024 条     │
└─────────────────────────────────────────────────────────────┘

10.4 运维 SOP

10.4.1 日常巡检

#!/bin/bash
# daily-check.sh - dqlite 日常巡检脚本

set -euo pipefail

echo "=== dqlite Daily Check - $(date) ==="

# 1. 检查节点存活
echo -e "\n[1] Node Health:"
for node in "127.0.0.1:9001" "127.0.0.1:9002" "127.0.0.1:9003"; do
    if nc -z -w2 ${node/:/ } 2>/dev/null; then
        echo "  ✓ $node - UP"
    else
        echo "  ✗ $node - DOWN"
    fi
done

# 2. 检查 Leader
echo -e "\n[2] Leader Status:"
# 通过客户端查询集群信息
echo "  (执行集群状态查询...)"

# 3. 检查磁盘使用
echo -e "\n[3] Disk Usage:"
for dir in /var/lib/dqlite/node{1,2,3}; do
    if [ -d "$dir" ]; then
        usage=$(du -sh "$dir" 2>/dev/null | cut -f1)
        echo "  $dir: $usage"
    fi
done

# 4. 检查日志
echo -e "\n[4] Recent Errors:"
journalctl -u dqlite --since "24 hours ago" --priority=err --no-pager | tail -5

# 5. 检查数据库大小
echo -e "\n[5] Database Size:"
find /var/lib/dqlite -name "*.db" -exec ls -lh {} \;

echo -e "\n=== Check Complete ==="

10.4.2 备份 SOP

#!/bin/bash
# backup.sh - dqlite 备份脚本

set -euo pipefail

BACKUP_DIR="/backup/dqlite/$(date +%Y%m%d_%H%M%S)"
mkdir -p "$BACKUP_DIR"

echo "Starting dqlite backup to $BACKUP_DIR..."

# 方式 1:使用 dqlite dump 工具
dqlite-dump \
    --address "127.0.0.1:9001" \
    --output "$BACKUP_DIR/dump.sql"

# 方式 2:复制数据文件(需要先暂停写入或使用一致性快照)
# 停止一个 Follower 节点,复制其数据目录
# systemctl stop dqlite-node-3
# cp -r /var/lib/dqlite/node3/* "$BACKUP_DIR/"
# systemctl start dqlite-node-3

# 压缩
gzip "$BACKUP_DIR/dump.sql"

# 上传到远程存储
# aws s3 sync "$BACKUP_DIR" "s3://backups/dqlite/$(basename $BACKUP_DIR)/"

# 清理 30 天前的备份
find /backup/dqlite -maxdepth 1 -mtime +30 -exec rm -rf {} \;

echo "Backup completed: $BACKUP_DIR"

10.4.3 恢复 SOP

#!/bin/bash
# restore.sh - dqlite 恢复脚本

set -euo pipefail

BACKUP_FILE="$1"
TARGET_ADDRESS="${2:-127.0.0.1:9001}"

if [ ! -f "$BACKUP_FILE" ]; then
    echo "Error: Backup file not found: $BACKUP_FILE"
    exit 1
fi

echo "=== dqlite Restore Procedure ==="
echo "Backup: $BACKUP_FILE"
echo "Target: $TARGET_ADDRESS"
echo ""
echo "WARNING: This will overwrite existing data!"
read -p "Continue? (yes/no): " confirm

if [ "$confirm" != "yes" ]; then
    echo "Aborted."
    exit 0
fi

# 1. 停止所有节点
echo "[1] Stopping all dqlite nodes..."
# systemctl stop dqlite-node-1 dqlite-node-2 dqlite-node-3

# 2. 清空数据目录
echo "[2] Clearing data directories..."
# rm -rf /var/lib/dqlite/node1/* /var/lib/dqlite/node2/* /var/lib/dqlite/node3/*

# 3. 在第一个节点恢复数据
echo "[3] Restoring data..."
# dqlite-restore --backup "$BACKUP_FILE" --data-dir /var/lib/dqlite/node1

# 4. 启动第一个节点
echo "[4] Starting first node..."
# systemctl start dqlite-node-1

# 5. 等待节点就绪
echo "[5] Waiting for node to be ready..."
# sleep 5

# 6. 重新初始化集群
echo "[6] Re-initializing cluster..."
# dqlite-add-node --leader 127.0.0.1:9001 --id 2 --address 127.0.0.1:9002
# dqlite-add-node --leader 127.0.0.1:9001 --id 3 --address 127.0.0.1:9003

# 7. 启动其他节点
echo "[7] Starting remaining nodes..."
# systemctl start dqlite-node-2 dqlite-node-3

echo ""
echo "Restore completed. Please verify data integrity."

10.4.4 滚动重启 SOP

#!/bin/bash
# rolling-restart.sh - 滚动重启 dqlite 集群

set -euo pipefail

NODES=("dqlite-node-1" "dqlite-node-2" "dqlite-node-3")

for node in "${NODES[@]}"; do
    echo "=== Restarting $node ==="

    # 1. 确认集群健康(当前节点不是唯一 Leader)
    echo "[1] Checking cluster health..."
    # ... 检查逻辑 ...

    # 2. 如果是 Leader,先转移
    echo "[2] Transferring leadership if needed..."
    # ... 转移逻辑 ...

    # 3. 停止节点
    echo "[3] Stopping $node..."
    systemctl stop "$node"

    # 4. 等待集群稳定
    echo "[4] Waiting for cluster to stabilize..."
    sleep 5

    # 5. 启动节点
    echo "[5] Starting $node..."
    systemctl start "$node"

    # 6. 等待节点加入集群
    echo "[6] Waiting for node to rejoin..."
    sleep 10

    # 7. 验证节点状态
    echo "[7] Verifying node status..."
    # ... 验证逻辑 ...

    echo "=== $node restarted successfully ==="
    echo ""
done

echo "All nodes restarted."

10.5 常见陷阱与避坑指南

10.5.1 部署陷阱

陷阱后果正确做法
使用偶数节点不增加容错,浪费资源始终使用 3、5、7 节点
跨机房部署Raft 对延迟敏感同机房或同城部署
使用 NFS 存储文件锁问题,数据损坏使用本地 SSD
不做备份数据丢失无法恢复定期备份 + 验证恢复
不同步时钟选举异常,日志不一致使用 NTP 同步

10.5.2 开发陷阱

陷阱后果正确做法
每条 INSERT 一个事务性能极差批量操作在同一事务中
不处理 BUSY 错误偶发失败实现指数退避重试
读取不走 Leader数据陈旧确认一致性需求
不使用参数绑定SQL 注入风险始终使用 ? 参数
连接池配置不当连接泄漏或过少合理配置 MaxOpenConns

10.5.3 运维陷阱

陷阱后果正确做法
不监控集群状态故障不可感知配置告警规则
不测试恢复流程真正恢复时手忙脚乱定期演练恢复
一次性变更多个节点Quorum 混乱一次只变更一个
不检查磁盘空间写入失败监控磁盘使用率
不更新版本错过安全补丁跟踪版本发布

10.6 性能调优速查表

优化项配置预期收益
批量写入100-500 条/事务10-100x
预编译语句Prepare + Execute2-3x
同步策略 NORMALPRAGMA synchronous=NORMAL2-3x
内存缓存PRAGMA cache_size=-16000读性能提升
内存映射PRAGMA mmap_size=256MB大数据集读提升
WAL 自动检查点PRAGMA wal_autocheckpoint=1000控制 WAL 大小
SSD 存储使用 NVMe SSD显著降低 I/O 延迟
同机房部署节点延迟 < 1ms降低写延迟

10.7 故障排查速查表

症状可能原因排查步骤解决方案
写入超时Leader 不可达1. 检查集群状态 2. 检查网络恢复 Leader 或网络
读取数据陈旧读了 Follower1. 确认读一致性需求配置 Leader 读
集群无法启动配置不一致1. 对比各节点配置统一配置
节点反复选举网络不稳定1. 检查节点间延迟改善网络
磁盘空间不足数据增长1. 检查数据库大小清理数据或扩容
内存占用高缓存配置过大1. 检查 PRAGMA cache_size减小缓存
快照失败I/O 错误1. 检查磁盘健康修复磁盘

10.8 生产检查清单

10.8.1 上线前检查

检查项状态说明
□ 节点数为奇数3、5、7
□ TLS 已启用双向认证
□ 防火墙已配置仅允许必要端口
□ 数据目录权限正确专用用户运行
□ 备份策略已实施定期备份 + 异地存储
□ 监控已部署Prometheus + Grafana
□ 告警规则已配置节点故障、Leader 丢失等
□ 日志收集已配置ELK / Loki
□ 时钟同步NTP 已配置
□ 存储类型正确SSD,非 NFS
□ 容量规划完成磁盘、内存、CPU
□ 恢复流程已测试定期演练
□ 滚动更新流程已验证无停机更新
□ 文档已更新运维手册

10.8.2 定期检查(每月)

检查项动作
数据库大小增长趋势预测何时需要扩容
备份恢复测试验证备份可用性
证书有效期提前 30 天续期
安全补丁更新到最新版本
性能基准测试确认性能未退化

本章小结

实践领域关键要点
技术选型dqlite 适合嵌入式 Linux 场景,数据量 < 10GB
容量规划磁盘 3 倍数据量,内存 = 数据量 × 0.1 + 256MB
监控节点存活、Leader 状态、延迟、磁盘、Raft 日志
备份每日备份 + 异地存储 + 定期恢复演练
避坑奇数节点、同机房、SSD、NTP、批量事务

全书完

恭喜你完成了 dqlite 分布式 SQLite 教程的全部 10 章学习!

快速回顾

章节内容
第 1 章dqlite 是什么,与 rqlite 对比
第 2 章安装编译,Docker 快速上手
第 3 章Raft 共识,日志复制,快照
第 4 章CRUD 操作,连接管理,事务
第 5 章C API,Go 绑定,错误处理
第 6 章集群搭建,Leader 选举,故障转移
第 7 章批量写入,读优化,同步策略
第 8 章TLS,认证,访问控制
第 9 章Docker,Kubernetes,持久化
第 10 章最佳实践,监控,运维 SOP

进一步学习

如有问题,欢迎在 dqlite GitHub Issues 中提问。