kevy
kevy 6.3.0

AI 系统的
数据层。

兼容 Redis——你的客户端不用改就能连。每一个操作都更快。向量检索、全文检索、索引、视图和变更流都在引擎里,而不是在围着它的四个服务里。这个终端是真的:同一个引擎,编译成 WebAssembly,就跑在这个标签页里。

Pick one — each builds its own rows, then asks a question of them.

kevy 6.3.0 · wasmstarting engine…

· 112 of the 191 server verbs — the embedded surface. Arrow keys walk history; a pasted block runs line by line.

它还能做什么

一个引擎。AI 系统需要的整个栈。

下面每一条命令,在这一页发布之前,都在 CI 里对着一台真实的服务器跑过。逐个点开——用 kevy 就是这个样子。

# an HNSW index over your keys — declared once,
# kept current by the write path
IDX.CREATE idx:sem ON PREFIX doc: FIELD vec TYPE vector KIND ann DIM 768 DISTANCE cosine M 16 EF 200

HSET doc:4410 title "Ada on pipelining" vec "<768 f32, little-endian>"

# nearest ten. no separate vector database, no sync job.
IDX.QUERY idx:sem KNN "<query vector>" LIMIT 10
-> 1) "doc:4410"
   2) "doc:9982"

embedding 由你给出;kevy 负责存它、索引它、检索它。引擎里没有模型,这是故意的。

Agent 记忆与 RAG →

IDX.CREATE idx:ft ON PREFIX doc: FIELD title TYPE str KIND text

IDX.QUERY idx:ft MATCH "pipelining"
-> 1) 1) "doc:1"
      2) "0.2877"          # BM25 score

# hybrid: fuse the text ranking with the vector ranking
IDX.QUERY HYBRID idx:ft MATCH "pipelining" idx:sem KNN "<vector>" LIMIT 20 RRFK 60

BM25,带中日韩分词,建在向量索引的同一批 key 上。

检索是怎么工作的 →

HSET order:1001 customer 881 status open  total 4400
HSET order:1002 customer 881 status paid  total 8400

IDX.CREATE idx:cust   ON PREFIX order: FIELD customer TYPE i64 KIND range
IDX.CREATE idx:status ON PREFIX order: FIELD status   TYPE str KIND range

# the read that would have been a SQL query
IDX.QUERY COMPOSE AND idx:cust EQ 881 idx:status EQ open
-> 1) "0"
   2) 1) 1) "order:1001"

带过滤的读始终是一次查表。没有查询计划器,没有扫描。

不靠数据库扛住读 →

# the answer, kept current by the WRITE path
VIEW.CREATE v:open881 QUERY ( AND idx:cust EQ 881 idx:status EQ open ) ORDER BY idx:cust

VIEW.QUERY v:open881
-> 1) "0"
   2) 1) "order:1001"  2) "881"

# reads never recompute it; writes keep it fresh

大多数应用向 ORM 真正索要的,就是这个。

物化视图 →

# a table is a declaration — compiled to named indexes, once
TABLE.DECLARE user PREFIX u: PK id COLUMN id str COLUMN name str COLUMN age i64 COLUMN dept str INDEX age range VALUES dept name ORDERPATH by_dept_age ON dept THEN age DESC

HSET u:1 id 1 name ada age 34 dept eng

# the ORDER BY dept, age DESC walk — one composite index, no planner
IDX.QUERY user.by_dept_age WHERE dept EQ eng LIMIT 20 FIELDS name age

类型化列、二级索引、复合 ORDER BY 路径——连你的 PG/MySQL schema 文件也能编译(kevy-cli sql compile)。没有运行期 SQL,没有 join:那些留在 Postgres。

单表服务型读 →

# kevy.toml — a RAM budget for the whole store
[tiering]
budget = "70%"               # or "4gb", or "auto"

# past the budget, the coldest values spill to a disk log
# and page back on access. a cold key is an ordinary key:
GET archive:2019:q3          # pays one disk read, same reply
TTL archive:2019:q3          # metadata answers from RAM
SCAN 0 MATCH archive:*       # sees cold keys — one key table

RAM 决定键的上限,磁盘决定数据的上限;AOF 持久化契约不动。v1 下沉字符串和 hash——list、set、stream 留在热层。

分层存储怎么工作 →

# tail every write from another process — or an agent.
# [feed] enabled = true in kevy.toml
FEED.SHARDS                 -> (integer) 16
FEED.TAIL 0                 -> 1) (integer) 1     # generation
                               2) (integer) 1     # offset
FEED.READ 0 1 0 COUNT 2     -> the writes themselves, replayable

偏移量可以续上。不用轮询,也不会漏。

变更流 →

# a 16-core server
cargo install kevy && kevy --port 6379

# inside your binary — no socket, no process
let db = Db::open("data/")?;
db.set(b"k", b"v", None)?;

# a browser tab — 481 KB, persists to OPFS
const db = await open({ persist: { name: "app" } });

# a microcontroller — no OS, no allocator
let mut store = Store::new_in(&mut arena);

四个地方,同一个引擎、同一批命令。

把 kevy 嵌进去 →

为什么可以直接替换 Redis

同样的协议。更高的吞吐。

RESP2 和 RESP3,206 条命令——redis-cli 和你的客户端库不用改就能连。一台机器,16 核,loopback,五次取中位数。

GET
7800299
5597865
SET
6918058
2573396
INCR
6133940
3459395
SADD
5600597
3690483
HSET
4287217
3021325
LPUSH
3213470
2862374
ZADD
3053101
2773929

kevy 6.3.0 · Redis 8.10.1 — LPUSH 和 ZADD 只领先 10% 和 15%。如果 list 或者 sorted set 是你的热路径,那么性能就不是换过来的理由。完整的表格在这里,valkey 和 Dragonfly 也一起打了。迁移只有三条命令——export、import、digest——而且两个方向都能走。

两分钟

  1. 安装

    一个二进制。没有运行时,也没有要解析的依赖。

    cargo install kevy
    kevy --port 6379
  2. 把你的客户端指过来

    你今天在用什么,就继续用什么——没有 kevy 客户端要装。node-redis / ioredisgo-redisStackExchange.Redisredis-pyhiredis 都能原样连上,kevy 自己的动词走同一个客户端的原始命令通道。这六种每次 push 都会在 CI 里对一台真实服务器跑同一套梯子。各语言示例(英文)。

    redis-cli -p 6379
    > SET greeting hello
    OK
    > TTL greeting
    (integer) -1
  3. 做一件 Redis 做不到的事

    声明一个索引;写入路径会把它维持在最新。

    IDX.CREATE idx:city ON PREFIX user: FIELD city TYPE str KIND range
    IDX.QUERY  idx:city EQ osaka

kevy 不会做的事

它不是集群。复制和故障切换有;把数据分片到多台机器上没有,以后也不会有。没有 AUTH,也没有 TLS——把它跑在内网,或者放在一个真正把这两件事做好的东西后面。多键写只在单个 shard 内原子,不是全局原子——跨 shard 的 RENAMEMSET 不是一步原子完成。每一处偏差都按命令逐条写下来了这里还写着什么时候根本不该用它