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

rqlite 完全指南 / 第 9 章:性能优化

第 9 章:性能优化

掌握 rqlite 的性能特征,优化读写性能、批量操作和连接管理。


9.1 性能特征概述

rqlite 的性能受以下因素影响:

因素影响程度说明
网络延迟⭐⭐⭐Raft 复制需要网络往返
磁盘 I/O⭐⭐⭐Raft 日志和 SQLite 写入
批量大小⭐⭐⭐批量操作显著提升吞吐量
一致性级别⭐⭐nonestrong
数据规模⭐⭐SQLite 索引和查询复杂度
节点数量更多节点 = 更多复制开销

典型性能数据

操作类型单条延迟批量(100条)延迟吞吐量
INSERT (单条)~2ms~500 ops/s
INSERT (批量)~20ms~5000 ops/s
SELECT (none)~0.5ms~2000 ops/s
SELECT (weak)~1ms~1000 ops/s
SELECT (strong)~2ms~500 ops/s
UPDATE (单条)~2ms~500 ops/s

提示: 以上数据基于 3 节点集群、同机房部署、数据量 < 1GB 的场景。


9.2 写入优化

9.2.1 批量写入

批量写入是提升 rqlite 写入性能的最有效手段:

# ❌ 逐条写入 — 性能差
for i in $(seq 1 100); do
    curl -s -XPOST 'localhost:4001/db/execute' \
        -H 'Content-Type: application/json' \
        -d "[[\"INSERT INTO logs (message) VALUES (?)\", \"log entry $i\"]]"
done
# 耗时:~200ms × 100 = ~20s

# ✅ 批量写入 — 性能好
stmts=""
for i in $(seq 1 100); do
    stmts+="{\"q\": \"INSERT INTO logs (message) VALUES (?)\", \"v\": [\"log entry $i\"]},"
done
stmts="${stmts%,}"  # 去掉最后的逗号

curl -s -XPOST 'localhost:4001/db/execute' \
    -H 'Content-Type: application/json' \
    -d "[${stmts}]"
# 耗时:~20ms

9.2.2 批量大小建议

批量大小延迟吞吐量适用场景
1~2ms~500/s实时单条写入
10~5ms~2000/s一般业务
50~12ms~4000/s高吞吐需求
100~20ms~5000/s数据导入
500~80ms~6000/s大批量导入
1000~150ms~6500/s极限批量

注意: 单次请求的语句数量不应超过 5000 条。过大的批次可能导致请求超时。

9.2.3 使用事务优化写入

-- 将多条写入放在一个事务中
BEGIN;
INSERT INTO logs (ts, msg) VALUES ('2026-05-10 10:00:00', 'msg1');
INSERT INTO logs (ts, msg) VALUES ('2026-05-10 10:00:01', 'msg2');
INSERT INTO logs (ts, msg) VALUES ('2026-05-10 10:00:02', 'msg3');
COMMIT;

rqlite 默认将单请求中的多条语句包装在事务中,无需显式 BEGIN/COMMIT

9.2.4 预编译语句缓存

虽然 rqlite 本身不缓存预编译语句,但客户端可以通过连接池复用 HTTP 连接来减少开销:

# Python — 复用 requests.Session
import requests

session = requests.Session()
session.auth = ('admin', 'password')

# 复用 TCP 连接
for batch in batches:
    session.post('http://localhost:4001/db/execute', json=batch)

9.3 读取优化

9.3.1 选择合适的一致性级别

# 场景 1: 统计查询(允许短暂不一致)→ none
curl -G 'localhost:4001/db/query' \
    --data-urlencode 'q=SELECT COUNT(*) FROM orders' \
    --data-urlencode 'level=none'

# 场景 2: 一般业务查询 → weak
curl -G 'localhost:4001/db/query' \
    --data-urlencode 'q=SELECT * FROM users WHERE id = 1' \
    --data-urlencode 'level=weak'

# 场景 3: 写后读 → strong
curl -G 'localhost:4001/db/query' \
    --data-urlencode 'q=SELECT * FROM users WHERE id = LAST_INSERT_ID()' \
    --data-urlencode 'level=strong'

9.3.2 使用索引优化查询

# 分析查询执行计划
rq 'q=EXPLAIN QUERY PLAN SELECT * FROM orders WHERE user_id = 1 AND status = "pending"'

索引设计原则:

原则说明示例
覆盖高频查询为 WHERE 条件创建索引CREATE INDEX idx ON orders(user_id)
复合索引顺序选择性高的列在前CREATE INDEX idx ON orders(status, user_id)
避免过多索引每个索引增加写入开销只创建必要的索引
使用覆盖索引索引包含所有查询列CREATE INDEX idx ON orders(user_id, status, product)
# 创建优化索引
rxe '[
    ["CREATE INDEX IF NOT EXISTS idx_orders_user_status ON orders(user_id, status)"],
    ["CREATE INDEX IF NOT EXISTS idx_orders_created ON orders(created_at DESC)"],
    ["CREATE INDEX IF NOT EXISTS idx_users_email ON users(email)"]
]'

9.3.3 查询优化技巧

# ❌ 避免 SELECT * — 只查需要的列
rq 'q=SELECT id, username, email FROM users WHERE id = 1'

# ❌ 避免 LIKE 前缀通配符
rq 'q=SELECT * FROM users WHERE username LIKE "zhang%"'  # ✅ 可以用索引
rq 'q=SELECT * FROM users WHERE username LIKE "%san%"'   # ❌ 无法用索引

# ✅ 使用 LIMIT 限制返回数据
rq 'q=SELECT * FROM orders ORDER BY created_at DESC LIMIT 20'

# ✅ 使用覆盖索引
rq 'q=SELECT user_id, status, COUNT(*) FROM orders GROUP BY user_id, status'

9.3.4 读写分流

利用 Follower 节点分担读取压力:

┌────────────────────────────────────────────┐
│              负载均衡器                      │
│   ┌──────────────┬──────────────┐          │
│   │ 写请求       │ 读请求       │          │
│   │ → Leader    │ → Follower  │          │
│   └──────┬───────┴──────┬──────┘          │
│          │              │                  │
│    ┌─────▼─────┐  ┌────▼──────┐          │
│    │  Leader   │  │ Follower  │          │
│    │  (node1)  │  │ (node2)   │          │
│    └───────────┘  └───────────┘          │
└────────────────────────────────────────────┘
# Nginx 配置示例 — 读写分流
upstream rqlite_write {
    server 10.0.1.10:4001;  # Leader
}

upstream rqlite_read {
    server 10.0.1.10:4001;  # Leader
    server 10.0.1.11:4001;  # Follower
    server 10.0.1.12:4001;  # Follower
}

server {
    # 写请求路由到 Leader
    location /db/execute {
        proxy_pass http://rqlite_write;
    }
    
    # 读请求路由到任意节点
    location /db/query {
        proxy_pass http://rqlite_read;
    }
}

9.4 索引优化策略

9.4.1 索引性能对比

# 无索引查询
rxe '[["DROP INDEX IF EXISTS idx_orders_user_status"]]'
# 10 万行数据,查询耗时 ~50ms

# 有索引查询
rxe '[["CREATE INDEX idx_orders_user_status ON orders(user_id, status)"]]'
# 10 万行数据,查询耗时 ~1ms

9.4.2 索引维护

# 查看表的索引
rq 'q=SELECT name, sql FROM sqlite_master WHERE type="index" AND tbl_name="orders"'

# 重建索引(碎片整理)
rxe '[["REINDEX idx_orders_user_status"]]'

# 删除未使用的索引
rxe '[["DROP INDEX IF EXISTS idx_unused"]]'

9.4.3 分析查询性能

# EXPLAIN QUERY PLAN
curl -s -G 'localhost:4001/db/query' \
    --data-urlencode 'q=EXPLAIN QUERY PLAN SELECT u.username, o.product FROM users u JOIN orders o ON u.id = o.user_id WHERE u.id = 1'

9.5 SQLite PRAGMA 调优

# 查看当前配置
rq 'q=PRAGMA cache_size'
rq 'q=PRAGMA page_size'
rq 'q=PRAGMA journal_mode'
rq 'q=PRAGMA synchronous'
rq 'q=PRAGMA temp_store'
PRAGMA默认值优化值说明
cache_size-2000-10000缓存页数,负值表示 KB
page_size40964096一般不需要改
journal_modeWALWALrqlite 默认启用
synchronousFULLNORMAL降低同步级别可提升性能
temp_storeDEFAULTMEMORY临时表使用内存

注意: PRAGMA 修改通过 rqlite API 执行后只对当前 Leader 的 SQLite 实例生效,不会通过 Raft 复制。启动参数才是可靠配置。


9.6 连接管理

9.6.1 HTTP 连接池

# Python — 使用 Session 复用连接
import requests
from requests.adapters import HTTPAdapter

session = requests.Session()
adapter = HTTPAdapter(
    pool_connections=10,    # 连接池大小
    pool_maxsize=20,        # 最大连接数
    max_retries=3           # 重试次数
)
session.mount('http://', adapter)
session.mount('https://', adapter)
session.auth = ('admin', 'password')
// Go — 自定义 Transport
import (
    "net/http"
    "time"
)

client := &http.Client{
    Transport: &http.Transport{
        MaxIdleConns:        100,
        MaxIdleConnsPerHost: 20,
        IdleConnTimeout:     90 * time.Second,
    },
    Timeout: 30 * time.Second,
}

9.6.2 超时配置

# 设置合理的超时
curl --connect-timeout 5 --max-time 30 \
    -XPOST 'localhost:4001/db/execute' \
    -H 'Content-Type: application/json' \
    -d '[["INSERT INTO ..."]]'

# 查询超时参数
curl -G 'localhost:4001/db/query' \
    --data-urlencode 'q=SELECT * FROM large_table' \
    --data-urlencode 'timeout=10s'

9.7 数据量优化

9.7.1 数据归档

# 将旧数据归档
rxe '[
    ["CREATE TABLE IF NOT EXISTS orders_archive AS SELECT * FROM orders WHERE 0=1"],
    ["INSERT INTO orders_archive SELECT * FROM orders WHERE created_at < \"2025-01-01\""],
    ["DELETE FROM orders WHERE created_at < \"2025-01-01\""],
    ["VACUUM"]
]'

9.7.2 定期 VACUUM

# VACUUM 回收已删除数据占用的空间
rxe '[["VACUUM"]]'

注意: VACUUM 会临时占用约两倍磁盘空间,建议在低峰期执行。


9.8 性能测试工具

9.8.1 简单基准测试脚本

#!/bin/bash
# rqlite-bench.sh — 简单的 rqlite 性能测试
HOST="${RQLITE_HOST:-localhost:4001}"
TOTAL=${1:-1000}
BATCH=${2:-50}

echo "=== rqlite 性能测试 ==="
echo "目标: $HOST"
echo "总记录数: $TOTAL"
echo "批次大小: $BATCH"
echo ""

# 准备表
curl -s -XPOST "http://$HOST/db/execute" \
    -H 'Content-Type: application/json' \
    -d '[["CREATE TABLE IF NOT EXISTS bench (id INTEGER PRIMARY KEY AUTOINCREMENT, data TEXT, ts DATETIME DEFAULT CURRENT_TIMESTAMP)"]]' > /dev/null

# 批量写入测试
echo "--- 批量写入测试 ---"
start=$(date +%s%N)
inserted=0
while [ $inserted -lt $TOTAL ]; do
    remaining=$((TOTAL - inserted))
    current_batch=$((remaining < BATCH ? remaining : BATCH))
    
    stmts=""
    for i in $(seq 1 $current_batch); do
        stmts+="{\"q\": \"INSERT INTO bench (data) VALUES (?)\", \"v\": [\"data-$inserted-$i\"]},"
    done
    stmts="${stmts%,}"
    
    curl -s -XPOST "http://$HOST/db/execute" \
        -H 'Content-Type: application/json' \
        -d "[${stmts}]" > /dev/null
    
    inserted=$((inserted + current_batch))
done
end=$(date +%s%N)
write_time=$(( (end - start) / 1000000 ))
write_tps=$(( TOTAL * 1000 / write_time ))
echo "  写入 $TOTAL 条: ${write_time}ms (${write_tps} ops/s)"

# 查询测试(不同一致性级别)
for level in none weak strong; do
    start=$(date +%s%N)
    for i in $(seq 1 100); do
        curl -s -G "http://$HOST/db/query" \
            --data-urlencode "q=SELECT COUNT(*) FROM bench" \
            --data-urlencode "level=$level" > /dev/null
    done
    end=$(date +%s%N)
    read_time=$(( (end - start) / 1000000 ))
    read_tps=$(( 100 * 1000 / read_time ))
    echo "  查询(100次,$level): ${read_time}ms (${read_tps} ops/s)"
done

# 清理
curl -s -XPOST "http://$HOST/db/execute" \
    -H 'Content-Type: application/json' \
    -d '[["DROP TABLE bench"]]' > /dev/null

echo ""
echo "测试完成"

9.9 优化检查清单

优化项检查内容优先级
批量写入每次请求包含多条语句⭐⭐⭐
索引优化高频查询字段有索引⭐⭐⭐
连接复用使用 HTTP 连接池⭐⭐⭐
一致性级别读请求使用合适的 level⭐⭐
读写分流读请求分发到 Follower⭐⭐
数据归档定期清理旧数据⭐⭐
VACUUM定期执行 VACUUM

9.10 本章小结

要点内容
批量写入最有效的性能优化手段,推荐 50-100 条/批
索引高频查询字段必须建索引
一致性根据业务需求选择最低一致级别
读写分流Follower 分担读取压力
连接管理使用 HTTP 连接池复用连接
定期维护VACUUM + 数据归档

上一章:第 8 章:安全配置 下一章:第 10 章:客户端开发