选型
你该用 kevy 吗?
有时候不该。下面按你真正做决定的顺序来:键值存储这个形状对不对,数据必须待在哪里,以及你要放弃什么。
第一步——键值存储这个形状对吗
当你已经知道 key 的时候,用 kevy。一个会话 id、一个用户 id、一个队列的名字、一个缓存 key。读是查表,不是提问。这覆盖的应用面比大多数人以为的要大——而且二级索引和物化视图,还能把一部分提问也变成查表。
当你的读确实是查询的时候,不要用 kevy。跨五张表的 join、临时的分析查询、跨无关行且要求真隔离的事务——那是 PostgreSQL 的活,也应该留给 PostgreSQL。我们把每一种关系型负载在这里的代价都写下来了,包括那些答案就是别搬的。
如果一台机器不够用,不要用 kevy。它没有集群模式,以后也不会有。单个 kevy 每秒能做几百万次操作,这个余量比大多数产品一辈子用到的都多——但越过它之后,你需要的是一个会分片的东西,那不是这里。
第二步——数据必须待在哪里
这一条决定你用哪种形态。每一行里的命令都是同一批。
| 你的情况 | 用哪种 | 为什么 |
|---|---|---|
| 多个服务共用这份数据 | 服务端 | 一个进程,在一个端口上说 RESP。你现有的 Redis 客户端不用改就能连。 |
| 数据只属于一个程序 | 嵌入 | 没有 socket,没有第二个进程,也没有东西要序列化。是一次函数调用,不是一次网络往返。 |
| 数据属于用户的设备 | 浏览器 | 151 KB 的 WebAssembly。真的 TTL,真的发布订阅,落在浏览器自己的文件系统上。离线也能用。 |
| 代码在边缘按请求执行 | 边缘 | 没有要预热的东西,也不用建连接。存储和你的代码待在同一个 isolate 里。 |
| 一台没有操作系统、没有堆的设备 | 裸机 | kevy-store 是 no_std 的:一块固定 arena,没有分配器。每次 push,CI 都会在 Cortex-M 上把它启动一遍。 |
你不会被锁死。嵌入式 API 和网络协议暴露的是同一组操作,所以一个程序如果长到进程内的存储装不下,它搬到服务端只需要改打开数据库的方式,而不必重写使用数据库的代码。
第三步——你要放弃什么
它真的能直接替换 Redis 吗?
在协议层面,是的——RESP2 和 RESP3,184 条命令,你的客户端库不会察觉。在行为层面,大体上是,而例外恰恰是重点。
SCAN 不是游标迭代器:一次调用就扫完整个 keyspace 并返回游标 0,所以常见的那个 SCAN 循环跑一次往返就结束了。ZRANK 是 O(N),因为我们的 sorted set 没有排名索引。SPOP 和 SRANDMEMBER 不随机——每次返回的都是同一批成员。跨 shard 的 RENAME 不是原子的。全部 184 条命令都标着真实的偏差和真实的代价,这些是从实现里读出来的,不是从 Redis 的文档里抄来的。机器挂了会怎么样?
每一次写都先落进一份 append-only 日志,启动时重放这份日志。在默认的
everysec fsync 策略下,被硬杀最多丢一秒的写;把 appendfsync = "always" 打开就一条都不丢,代价是吞吐。快照存在的唯一目的,是给重放时间设一个上界。持久化指南里有具体数字。机器故障能扛过去吗?
能——一主 N 从,带真正的故障切换:计划内的主从交接、带 epoch 围栏的崩溃选举,以及一个可选的一致性阶梯(
WAIT、read-your-writes 令牌、有界陈旧度)。你得不到的是把数据分片到多台机器上。副本是拷贝,不是切片。可用性指南把哪些写能挺过一次故障切换、哪些挺不过,都写明白了。有认证吗?
没有,以后也不会有。没有 AUTH,没有 ACL,没有 TLS——永久不在范围内。把 kevy 跑在内网,或者放在一个真正把这些事做好的代理后面。一层敷衍的认证比坦白没有认证更糟,因为它会引诱人去信任它。
如果我用得太大了,或者只是改主意了呢?
kevy-cli export 会把你的 keyspace 写成一个普通的 RESP 文件,任何 Redis 兼容的服务端都能导入它;kevy-cli digest 则在你扔掉任何东西之前,先证明这份拷贝没有走样。迁移指南里,搬出去写得和搬进来一样细。
还是拿不准?
打开 playground。那是一个真的 kevy 引擎,编译成 WebAssembly 跑在你的标签页里——写几个 key,看 TTL 到点消失,翻一翻躺在你自己磁盘上的 append-only 日志。没有一样是预先录好的,也没有任何服务端参与。