kevy4.0

二级索引(IDX.* / idx_*

kevy 可以在键空间的一个前缀域上维护声明式二级索引:该前缀下的每个 hash 键是一“行”,其中一个声明过的 hash 字段是被索引的值。索引与每一次写入同步维护(构造即派生——索引不可能与数据发生漂移,而 IDX.VERIFY 让这句话可被证伪),查询侧支持游标分页、双索引组合,以及可选的字段补水。

IDX.CREATE idx_age ON PREFIX user: FIELD age TYPE i64 KIND range
HSET user:42 age 31 name "……"
IDX.QUERY idx_age RANGE 18 30 LIMIT 100 FIELDS name

声明

IDX.CREATE <name> ON PREFIX <p> FIELD <f> TYPE i64|f64|str KIND range|unique [MAXMEM <bytes>]

查询

唯一性是围栏,不是锁

unique 索引不阻塞写入——在写入时强制全局唯一,就意味着把跨 shard 的写串行化。它的做法是:重复项被计数(VERIFY / LIST 里的 duplicates),并且在 EQ 读取时以多命中的形式暴露出来。如果你需要的是硬唯一性,就在集群模式下用 {hashtag} 前缀把这个域钉到单个 shard 上,或者在 MULTI / WATCH 下做先查后写。

Embedded

同一台引擎,类型化 API:idx_create / idx_drop / idx_query / idx_count / idx_stats / idx_list(值是 IndexValue,游标是 IndexCursor)。没有 FIELDS 补水——你人在进程内,字段直接用 hget 读。idx_create 同步构建,返回即代表索引可服务。

一致性与成本模型

聚合 kind(KIND agg)——写入时 GROUP BY

IDX.CREATE ord_amt ON PREFIX ord: FIELD amount TYPE i64 KIND agg GROUPBY status
IDX.QUERY ord_amt GROUP paid                      → [count, sum, min, max, avg]
IDX.QUERY ord_amt GROUPS BY sum LIMIT 100         → ranked [group, count, sum, min, max]

这是引擎对 SELECT g, COUNT(*), SUM(v) … GROUP BY g 的回答:聚合值在写路径上维护(一条声明过的访问路径——绝不是查询期的全行扫描)。min / max 借助逐组的值多重集,在删除下仍保持精确;sum 用 f64 累加(精度边界见文档);值强制转换失败、或分组字段缺失的行,被排除并计数(VERIFY 可见)。跨 shard 归并是精确的:count / sum 相加,极值取极。GROUPS 按 count / sum / max 降序或 min 升序排名,LIMIT ≤ 1000。

没有 HAVING、没有聚合表达式、没有近似 sketch——那是查询语言的斜坡。GROUPS 的结果拿回应用里过滤。

Embedded:idx_create_agg(name, prefix, field, ty, group_by) / idx_group(name, g) / idx_groups(name, by, limit)

内存 ≈ groups × (gkey+64) + distinct_values × 18 + rows × (key+10)(常数是对着实测 RSS 校准出来的);bench/agggate.sh 拿它跟真实 RSS 对钳,同时钳住 GROUP p99 < 1ms @ 100 万行 × 1 万组、GROUPS top-100 < 5ms、写入税 < 10%。