兼容 Redis——你的客户端不用改就能连。每一个操作都更快。向量检索、全文检索、索引、视图和变更流都在引擎里,而不是在围着它的四个服务里。这个终端是真的:同一个引擎,编译成 WebAssembly,就跑在这个标签页里。
Pick one — each builds its own rows, then asks a question of them.
· 112 of the 191 server verbs — the embedded surface. Arrow keys walk history; a pasted block runs line by line.
下面每一条命令,在这一页发布之前,都在 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 负责存它、索引它、检索它。引擎里没有模型,这是故意的。
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 60BM25,带中日韩分词,建在向量索引的同一批 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 tableRAM 决定键的上限,磁盘决定数据的上限;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);四个地方,同一个引擎、同一批命令。
RESP2 和 RESP3,206 条命令——redis-cli 和你的客户端库不用改就能连。一台机器,16 核,loopback,五次取中位数。
kevy 6.3.0 · Redis 8.10.1 — LPUSH 和 ZADD 只领先 10% 和 15%。如果 list 或者 sorted set 是你的热路径,那么性能就不是换过来的理由。完整的表格在这里,valkey 和 Dragonfly 也一起打了。迁移只有三条命令——export、import、digest——而且两个方向都能走。
一个二进制。没有运行时,也没有要解析的依赖。
cargo install kevy
kevy --port 6379你今天在用什么,就继续用什么——没有 kevy 客户端要装。node-redis / ioredis、go-redis、StackExchange.Redis、redis-py、hiredis 都能原样连上,kevy 自己的动词走同一个客户端的原始命令通道。这六种每次 push 都会在 CI 里对一台真实服务器跑同一套梯子。各语言示例(英文)。
redis-cli -p 6379
> SET greeting hello
OK
> TTL greeting
(integer) -1声明一个索引;写入路径会把它维持在最新。
IDX.CREATE idx:city ON PREFIX user: FIELD city TYPE str KIND range
IDX.QUERY idx:city EQ osaka它不是集群。复制和故障切换有;把数据分片到多台机器上没有,以后也不会有。没有 AUTH,也没有 TLS——把它跑在内网,或者放在一个真正把这两件事做好的东西后面。多键写只在单个 shard 内原子,不是全局原子——跨 shard 的 RENAME 或 MSET 不是一步原子完成。每一处偏差都按命令逐条写下来了,这里还写着什么时候根本不该用它。