这是两场完全不同的对话。从 Redis 过来,协议是一样的,要问的是哪些行为不一样。从关系型数据库过来,没有一处是一样的,要问的是这份负载里到底哪一部分该搬——答案是只搬一部分,而且我们会说清楚是哪一部分。
你的客户端不用改。kevy 说 RESP2 和 RESP3,实现了 206 条命令。把你现有的库指过来就行,代码不动,redis-cli 不换。没有新的 SDK 要接,也没有新协议要学。
所以真正要问的只有一句:你能换到什么。只有四样。如果这四样对你都没有价值,那就留在 Redis——它是一件极好的软件,为换而换,只是白白搭进去一个星期。
嵌进二进制,发到浏览器标签页,在一颗没有分配器的 Cortex-M 上启动。今天这些场合各要一层自己的存储、一套自己的 API;在这里,它们是同一个引擎、同一批命令。如果你曾经为客户端那边另写过一个缓存,这就是值得看一眼的理由。
二级索引、物化视图、向量 KNN、BM25 全文检索都在引擎里——不是模块,不是 sidecar,也不是一份会慢慢和原数据对不上的副本。原本要跑 Redis 加一个搜索集群的团队,往往只需要跑一个东西。
# look up by a field, not just by the key
IDX.CREATE idx:city ON PREFIX user: FIELD city TYPE str KIND range
IDX.QUERY idx:city EQ osaka
# vectors, in the same engine, over the same keys
IDX.CREATE idx:sem ON PREFIX doc: FIELD vec TYPE vector KIND ann DIM 768 DISTANCE cosine
IDX.QUERY idx:sem KNN "<vector>" LIMIT 10同一台机器上对 Redis 8.10.1:GET 快 1.33×,SET 快 2.66×,INCR 快 2.05×。不过在你把这个当成理由之前,先把整张表看完——LPUSH 和 ZADD 只领先 10% 和 15%,如果 list 或者 sorted set 是你的热路径,那这就不是你该搬的理由。
给 store 一个 RAM 预算,最冷的值就下沉到磁盘上一份可丢弃的 value log,访问时换回——冷键上每条命令不变,append-only 日志的持久化契约不动。RAM 决定你能放多少个键,磁盘决定你能放多少数据。这替掉的是「Redis 加一个单独的磁盘存储」的拆分——大 value、长尾负载不用再分家。诚实的边界:它默认关闭,v1 只下沉字符串和 hash(list、set、stream 留在热层),64 字节以下的值从不下沉——stub 会和值一样大。
# kevy.toml
[tiering]
budget = "70%" # or "4gb", or "auto"没有集群。副本是拷贝,不是分片。没有 AUTH,没有 TLS。还有几条命令的行为不一样——最要紧的一条:多键写只在单个 shard 内原子(一次调用就把全部返回,游标为 0),跨 shard 的 RENAME 不是原子的。这些都不是 bug,而且每一条都在命令文档里写着。请在决定之前把这份清单读完,不要等决定之后:每条命令真实的代价和真实的偏差。
# 1. dump what you want to move. it is a RESP file — readable,
# diffable, and it streams rather than loading into memory.
kevy-cli export -p 6379 --prefix user: dump.resp
-> exported 41023 keys -> dump.resp
# 2. load it. --strict stops on the first error rather than
# limping onward with a half-migrated keyspace.
kevy-cli import -p 6380 --strict dump.resp
-> imported 82046 ok, 0 errors, offset 4108331
# 3. prove they agree, rather than hoping.
kevy-cli digest -p 6379 user:
kevy-cli digest -p 6380 user:
-> 41023 keys 3bca92aa52269300 # the same hash, or you did not migrate
# an interrupted import resumes where it stopped:
kevy-cli import -p 6380 --resume dump.resp从 Redis 导出,导入 kevy,再校验两边一致。下面每一条命令都真的跑过。
不要搬你的数据库。要搬的是它身上那一部分从来就不是数据库问题的东西。
会话。限流。功能开关。任务队列。还有那一行每个请求都要读、却从来没人拿去 join 的热点行。在大多数应用里,这些都住在 Postgres 里,而它们正是被敲得最狠的那些行——不是因为关系型数据库做不好,而是因为它们从来就不是提问。它们是查表。key 你早就知道了。
Postgres 最擅长的那些事,留给 Postgres——join、临时查询、分析,以及跨无关行的真隔离事务。kevy 接走对外服务的那条路径,让数据库喘口气。
而且单表的服务型读也能搬。用 TABLE.DECLARE 把类型化列、二级索引和复合 ORDER BY 路径声明一次——或者用 kevy-sql 直接编译你手上现成的 PG/MySQL schema 文件——单张表的读路径(索引化的 WHERE、余下的过滤、ORDER BY、翻页、COUNT)就编译到 kevy 索引上,查询期没有任何计划器。kevy-sql 是构建期的编译器,不是 SQL 引擎:join 和临时 SQL 被按名拒绝,它们留在 Postgres。这正是大多数应用真正在用 ORM 的那一部分。
按负载逐条来。红色的那三行,是最多人搞错的地方。
| 负载 | 搬吗 | 为什么 |
|---|---|---|
| 会话、令牌 | *搬 | 按 key 查表,带一个 TTL。数据库一直是在帮你的忙,那本来就不是它的活。 |
| 限流、计数器 | *搬 | 带过期的 INCR 是原子的,而且 O(1)。在 SQL 里,这是压在你最热那一行上的行锁。 |
| 任务队列 | *搬 | list 和 stream,带消费组和逐条确认。所谓队列表,不过是一套加了额外步骤的加锁约定。 |
| 功能开关、配置 | *搬 | 一直在读,很少写,从来不 join。 |
| 单表读(过滤、排序、翻页) | *搬 | 把表的访问路径声明一次——或者用 kevy-sql 编译你的 schema 文件——索引化的 WHERE + ORDER BY + LIMIT 这类读始终是一次查表。见用索引扛住读。 |
| 聚合(计数、合计) | *多数该搬 | 物化视图在写入路径上就把它更新好,不必每次读都重算一遍。 |
| 跨多张表的 join | !不要搬 | kevy 没有 join,以后也不会长出 join。这正是 Postgres 存在的意义。 |
| 分析、临时查询 | !不要搬 | 这里没有查询计划器,也没有优化器。不要尝试。 |
| 跨无关行的事务 | !不要搬 | MULTI 是按 shard 生效的,不是全局的。如果你需要跨整个 keyspace 的可串行化隔离,你需要的是数据库。 |
红色那三行不是待办清单,是拒绝——kevy 不会长出 join,也不会长出优化器,因为把这两样做砸了,比不做还糟。每一种关系型负载,以及它在这里真实的代价,其中也包括那些诚实答案就是留在 Postgres 里的负载。
# 1. pick ONE workload. sessions are the usual first, because
# nothing joins against them and losing one is survivable.
# 2. write to both for a week. reads still come from Postgres.
# you are checking that the shapes match, not that it is fast.
# 3. flip reads to kevy. keep the dual write.
redis-cli SET session:$SID "$JSON" EX 3600
# 4. when it has been boring for a fortnight, drop the table.
# then do the next workload. rate limits, then queues, then
# whichever of your read paths a secondary index can answer.不要一次性切换。一次只搬一种负载,让数据库继续做事实来源,然后测。
同样这三条命令,反过来跑一遍就行。kevy-cli export 写出的是一个普通的 RESP 文件,任何 Redis 兼容的服务端都能导入,而 digest 能证明这份拷贝没有走样。迁移指南里,搬出去这件事写得和搬进来一样细——我们宁可你走得干净,也不希望你是因为被卡住才留下。