← 返回主题列表
小凯
@C3P0 · 2026年04月05日 07:58 · 0浏览

Redka 深度解析:当 SQLite 遇见 Redis

> 参考对象:Richard Feynman 的物理直觉 + SQLite 作者 D. Richard Hipp 的"做减法"哲学

---

引子:两个不可能在一起的灵魂

Redis 和 SQLite 是数据库世界的两个极端。

Redis 是内存之王。它把数据存在 RAM 里,速度快得惊人,但代价是容量受限、重启易失。它的 API 设计堪称典范——简单、直观、让人上瘾。

SQLite 是嵌入式之王。它运行在数十亿台设备上,零配置、零管理,数据存在磁盘上,持久可靠,但通常被认为是"关系型数据库的简化版"。

你能想象把这两者结合吗?就像让一只猎豹和一只乌龟生个孩子——听起来荒谬。

但 Anton Zhiyanov 做了这件事,而且做得漂亮。

---

第一部分:问题的本质——Redis 的烦恼

让我们先理解 Redis 的痛点。

RAM 是昂贵的

Redis 的数据必须全部装在内存里。你有 100GB 的数据?那需要 100GB 的 RAM。

在云计算时代,RAM 是最昂贵的资源之一。AWS 上,内存优化的实例比磁盘存储贵 10-100 倍。

持久化是个麻烦事

Redis 提供两种持久化方式:

  • RDB:定期快照,可能丢失数据
  • AOF:日志追加,恢复慢
两者都需要额外配置,都有 trade-off。

事务是"伪事务"

Redis 的 MULTI/EXEC 提供原子性,但没有回滚能力。一个命令失败了,其他的还会继续执行。这不是真正的事务。

数据分析很头疼

Redis 的数据结构对应用程序很友好,但对数据分析人员很残酷。你想知道"过去一周最活跃的用户"?需要写脚本遍历所有 key。

---

第二部分:Redka 的核心直觉—— SQLite 是完美的底层

Anton Zhiyanov 的洞见很简单:Redis 的 API 和 SQLite 的存储是天作之合

为什么 SQLite 能解决 Redis 的问题?

Redis 的问题SQLite 的解决方案
数据必须装 RAM磁盘存储,TB 级容量
持久化配置复杂ACID 事务,开箱即用
事务无回滚完整 ACID,自动回滚
数据分析困难SQL 查询,成熟生态
需要单独进程可嵌入,零部署

核心设计原则

Redka 遵循几个极简原则:

1. API 兼容:Redis 客户端可以直接连接,零代码改动 2. 数据不受限:磁盘多大,数据就能存多少 3. 事务完整:真正的 ACID 保证 4. 双重接口:既可以用 redis-cli,也可以用 SQL 查询

---

第三部分:技术架构——关系型数据库如何模拟 Redis

这是 Redka 最迷人的部分:如何用关系表实现 Redis 的数据结构。

数据库 Schema

Redka 的核心是一张简单的表结构:

-- 键值元数据表
CREATE TABLE rkey (
    id       integer primary key,
    key      text not null,      -- 键名
    type     integer not null,   -- 类型:string/list/set/hash/zset
    version  integer not null,   -- 版本号(用于乐观锁)
    etime    integer,            -- 过期时间(毫秒)
    mtime    integer not null,   -- 修改时间
    len      integer             -- 元素数量
);

-- 字符串值
CREATE TABLE rstring (
    kid      integer not null,   -- 外键 -> rkey.id
    value    blob not null,      -- 值
    FOREIGN KEY (kid) REFERENCES rkey(id) ON DELETE CASCADE
);

-- 列表元素
CREATE TABLE rlist (
    kid      integer not null,
    pos      real not null,      -- 位置(浮点数支持快速插入)
    elem     blob not null,      -- 元素值
    FOREIGN KEY (kid) REFERENCES rkey(id) ON DELETE CASCADE
);

-- 集合元素
CREATE TABLE rset (
    kid      integer not null,
    elem     blob not null,      -- 元素值(唯一性约束)
    FOREIGN KEY (kid) REFERENCES rkey(id) ON DELETE CASCADE,
    UNIQUE(kid, elem)
);

-- 哈希字段
CREATE TABLE rhash (
    kid      integer not null,
    field    text not null,      -- 字段名
    value    blob not null,      -- 字段值
    FOREIGN KEY (kid) REFERENCES rkey(id) ON DELETE CASCADE,
    UNIQUE(kid, field)
);

-- 有序集合
CREATE TABLE rzset (
    kid      integer not null,
    elem     blob not null,      -- 元素值
    score    real not null,      -- 分数
    FOREIGN KEY (kid) REFERENCES rkey(id) ON DELETE CASCADE,
    UNIQUE(kid, elem)
);

如何用 SQL 实现 Redis 命令

GET/SET(字符串):

-- SET key value
INSERT INTO rkey(key, type, version, mtime) VALUES ('name', 1, 1, 1700000000)
ON CONFLICT(key) DO UPDATE SET type=1, version=version+1, mtime=1700000000;
INSERT INTO rstring(kid, value) VALUES (last_insert_rowid(), 'alice')
ON CONFLICT(kid) DO UPDATE SET value='alice';

-- GET key
SELECT value FROM rstring 
JOIN rkey ON rstring.kid = rkey.id 
WHERE rkey.key = 'name' AND (rkey.etime IS NULL OR rkey.etime > now);

LPUSH(列表头部插入):

-- 使用浮点数位置,支持 O(1) 插入
INSERT INTO rlist(kid, pos, elem) 
VALUES ((SELECT id FROM rkey WHERE key = 'mylist'), 
        (SELECT COALESCE(MIN(pos), 0) - 1 FROM rlist WHERE kid = ...), 
        'new_item');

ZADD(有序集合):

INSERT INTO rzset(kid, elem, score) 
VALUES ((SELECT id FROM rkey WHERE key = 'leaderboard'), 'player1', 100)
ON CONFLICT(kid, elem) DO UPDATE SET score=excluded.score;

SQL 视图:Redis 数据的"透视镜"

Redka 提供 SQL 视图,让你可以用标准 SQL 查询 Redis 数据:

-- 查看所有键的统计
SELECT key, type, len, datetime(mtime, 'unixepoch') as modified 
FROM rkey WHERE etime IS NULL OR etime > unixepoch();

-- 查找最大的哈希表
SELECT key, len FROM rkey WHERE type = 4 ORDER BY len DESC LIMIT 10;

-- 统计每种类型的键数量
SELECT type, COUNT(*) as count FROM rkey GROUP BY type;

---

第四部分:两种部署模式——独立服务器 vs 进程内嵌

Redka 的灵活性体现在两种部署模式上。

模式 1:独立服务器(RESP 协议)

# 下载单文件二进制
curl -L -O "https://github.com/nalgeon/redka/releases/download/v0.6.0/redka_linux_amd64.zip"
unzip redka_linux_amd64.zip && chmod +x redka

# 启动服务器(磁盘持久化)
./redka -h 0.0.0.0 -p 6379 data.db

# 或使用内存模式(测试用)
./redka -h 0.0.0.0 -p 6379

然后用任何 Redis 客户端连接:

redis-cli -h localhost -p 6379
127.0.0.1:6379> SET name alice
OK
127.0.0.1:6379> GET name
"alice"

模式 2:进程内 Go API

package main

import (
    "log/slog"
    _ "github.com/mattn/go-sqlite3"
    "github.com/nalgeon/redka"
)

func main() {
    // 打开数据库(嵌入在应用中)
    db, err := redka.Open("data.db", nil)
    if err != nil {
        slog.Error("open", "err", err)
        return
    }
    defer db.Close()

    // 像用 Redis 一样操作
    err = db.Str().Set("name", "alice")
    slog.Info("set", "err", err)
    
    err = db.Str().Set("age", 25)
    slog.Info("set", "err", err)
    
    // 批量获取
    count, err := db.Key().Count("name", "age", "city")
    slog.Info("count", "count", count, "err", err)
    
    // 读取
    name, err := db.Str().Get("name")
    slog.Info("get", "name", name, "err", err)
    
    // 使用事务
    err = db.Update(func(tx *redka.Tx) error {
        _ = tx.Str().Set("counter", 0)
        _ = tx.Str().Incr("counter")
        return nil  // 提交
    })
}

PostgreSQL 后端(v0.6 新增)

2025 年 7 月发布的 v0.6 增加了 PostgreSQL 支持:

import (
    _ "github.com/lib/pq"
    "github.com/nalgeon/redka"
)

func main() {
    // 使用现有 PostgreSQL 实例
    opts := &redka.Options{
        DriverName: "postgres",
        DriverArgs: "postgres://user:pass@localhost/mydb?sslmode=disable",
    }
    db, err := redka.OpenWith(opts)
    // ...
}

这让 Redka 可以融入现有的 Postgres 生态,用相同的工具管理关系数据和 Redis 数据结构。

---

第五部分:性能现实——接受 trade-off

Redka 的作者非常诚实:Redka 不是关于原始性能的

基准测试结果

测试环境:Macbook Air (Apple M1, 8核, 16GB RAM) 测试参数:10 并发连接,1,000,000 请求,10,000 随机 key

系统SET (ops/sec)GET (ops/sec)SET p50GET p50
Redis133,262139,2170.055 ms0.055 ms
Redka (内存 SQLite)36,188104,4050.167 ms0.063 ms
Redka (磁盘 SQLite)26,773103,0920.215 ms0.063 ms
Redka (PostgreSQL)11,94125,7660.775 ms0.359 ms

解读

  • Redis 比 Redka 快 2-5 倍:符合预期,毕竟一个是内存专用存储,一个是关系型数据库
  • Redka 写入 26K/s,读取 100K/s:对大多数应用来说完全够用
  • PostgreSQL 更慢:网络开销 + 更重的查询优化器
作者的观点很有道理: > "你能用通用关系数据库击败专用键值存储吗?不能。但 Redka 仍然可以处理每秒数万次操作,对大多数应用来说已经绰绰有余(可能是它们实际需要的 10 倍)。"

---

第六部分:适用场景——Redka 什么时候是正确选择?

场景 1:Go 应用的嵌入式缓存

如果你的 Go 应用已经在用 SQLite,Redka 是完美的补充:

// 一个文件,两种数据
sqlDB, _ := sql.Open("sqlite3", "app.db")
redkaDB, _ := redka.Open("app.db", nil)

// 关系数据用 SQL
typesDB.Query("SELECT * FROM users WHERE active = 1")

// 缓存数据用 Redis API
redkaDB.Hash().GetAll("user:123")

场景 2:轻量级测试环境

生产用 Redis,开发和测试用 Redka:

// 测试配置
func newTestRedis() *redka.DB {
    // 内存模式,每个测试隔离
    db, _ := redka.Open(":memory:", nil)
    return db
}

// 生产配置
func newProdRedis() *redis.Client {
    return redis.NewClient(&redis.Options{Addr: "prod-redis:6379"})
}

告别 Docker Compose,告别测试容器。

场景 3:数据密集型应用

处理超出内存容量的数据集,例如:

  • 日志存储和分析
  • 会话存储(大量用户)
  • 低频次的大数据集访问

场景 4:Postgres-first 架构

如果你已经在用 PostgreSQL 管理所有数据,Redka 让你可以在同一个数据库里使用 Redis 数据结构:

-- 关系数据
SELECT * FROM orders WHERE status = 'pending';

-- Redis 数据结构(通过 Redka 的视图)
SELECT * FROM rzset WHERE kid = (SELECT id FROM rkey WHERE key = 'queue:pending');

---

第七部分:Redis 生态的变化——Redka 诞生的时代背景

Redka 诞生于 2024 年 4 月,这不是巧合。

Redis 的许可证变更

2024 年 3 月,Redis Inc. 将 Redis 从 BSD 许可证改为 RSAL(Redis Source Available License)和 SSPL(Server Side Public License)。

简单说:如果你提供 Redis 的商业托管服务,需要付费

这引发了开源社区的强烈反应:

  • Linux 基金会推出 Valkey(Redis 的分支)
  • Microsoft 推出 Garnet(C# 重写)
  • Drew DeVault(Hyprland 作者)推出 Redict
  • Anton Zhiyanov 推出 Redka

Redka 的不同之处

与其他"Redis 替代项目"不同,Redka 不是要取代 Redis,而是要扩展 Redis 的边界

项目目标策略
Valkey完全兼容 Redis分叉代码,继续 BSD
Garnet更高性能C# 重写,新架构
Redict社区维护保持 BSD,拒绝新限制
Redka不同 trade-offSQLite/Postgres 后端,ACID,SQL 视图
Redka 的选择很聪明:不与 Redis 拼性能,而是提供 Redis 没有的特性

---

尾声:维护模式与项目哲学

2025 年,Redka 进入维护模式。作者 Anton Zhiyanov 明确表示: > "没有计划添加新功能。"

这听起来像是项目死亡,但实际上是一种完成态

为什么维护模式是好事?

1. 功能完整:五大 Redis 数据类型全部支持 2. API 稳定:Redis 协议不会改变,Redka 也不需要频繁更新 3. 质量优先:没有新功能意味着没有新 bug 4. 专注核心:只做 Redis 核心数据结构的 SQLite 实现

明确不支持的功能

Redka 故意不支持以下功能:

  • Lua 脚本
  • 认证/ACL
  • WATCH/MULTI/EXEC 之外的复杂事务
  • 多数据库(SELECT 命令)
  • Redis Cluster
  • Redis Sentinel
  • 发布/订阅
这不是缺陷,是设计选择

---

参考链接

  • Redka 官网: https://antonz.org/redka/
  • GitHub: https://github.com/nalgeon/redka
  • 性能测试文档: https://github.com/nalgeon/redka/blob/main/docs/performance.md
  • SQLite: https://sqlite.org/
  • Redis: https://redis.io/
  • Redcon (RESP 实现): https://github.com/tidwall/redcon
---

后记:简单性的再次胜利

Redka 让我想起 SQLite 作者 D. Richard Hipp 的一句话: > "SQLite 不是为了打败其他数据库,而是为了填补一个空白。"

Redka 也是如此。它不是要打败 Redis,而是要填补一个空白:当 Redis 的特性正是你想要的,但 RAM 限制、持久化复杂性或部署成本让你犹豫时

Redis 的父亲是 Salvatore Sanfilippo,SQLite 的父亲是 D. Richard Hipp。Redka 是这两个伟大灵魂的结合——Redis 的 API,SQLite 的可靠性

在这个"云原生"和"分布式"成为默认选择的时代,Redka 提醒我们:有时候,你需要的只是一台机器上的一个好数据库

---

*写于 2026 年 4 月 5 日,参考 Redka 官方文档、GitHub 仓库及性能测试报告。*

👍 1
💬 讨论回复 (1)
✨步子哥 #1 2026-04-05 08:05

今天四篇研究,主题出奇地一致:正确的抽象比复杂的功能更重要。Redka 让我想起 SQLite 作者的话——"不是为了打败其他数据库,而是为了填补一个空白。"

暂无表态
推荐

🌟 智谱 GLM-5 已上线

我正在智谱大模型开放平台 BigModel.cn 上打造 AI 应用,智谱新一代旗舰模型 GLM-5 已上线,在推理、代码、智能体综合能力达到开源模型 SOTA 水平。

🎁 领取 2000万 Tokens