kevy4.0

视图(VIEW.* / view_*

视图是若干声明过的索引的具名组合——一棵由索引形状构成的 AND / OR / DIFF 树,外加一个排序索引——可以作为一个整体来查询,要么每次查询时求值(virtual),要么在每次写入时增量维护(materialized,可选择用 top-K 封顶)。视图是引擎对“热点列表”查询的回答——WHERE state = 'ready' AND pri BETWEEN 0 AND 100 ORDER BY pri DESC LIMIT 10——把它做成一条声明过的访问路径,而不是查询期的扫描。

IDX.CREATE j_pri  ON PREFIX job: FIELD pri  TYPE i64 KIND range
IDX.CREATE j_state ON PREFIX job: FIELD state TYPE str KIND range

VIEW.CREATE ready_jobs
    QUERY ( AND j_pri RANGE 0 100 j_state EQ ready )
    ORDER BY j_pri DESC
    MODE materialized TOPK 100
VIEW.QUERY ready_jobs LIMIT 10

快速上手(服务器)

视图要组合的一切都必须先存在——叶子和 ORDER BY 索引,都是在同一个前缀域上声明过的 IDX.* 索引(indexes.md):

kevy-cli -p 6004 IDX.CREATE j_pri ON PREFIX job: FIELD pri TYPE i64 KIND range
kevy-cli -p 6004 IDX.CREATE j_state ON PREFIX job: FIELD state TYPE str KIND range
kevy-cli -p 6004 VIEW.CREATE ready_jobs QUERY ( AND j_pri RANGE 0 100 j_state EQ ready ) ORDER BY j_pri DESC MODE materialized TOPK 100
kevy-cli -p 6004 HSET job:1 pri 10 state ready
kevy-cli -p 6004 HSET job:2 pri 99 state blocked
kevy-cli -p 6004 VIEW.QUERY ready_jobs LIMIT 10

回复按视图顺序分页给出 key, order-value 对,并带一个可续读的 CURSOR。树的语法(一行,带括号):

tree     = '(' AND|OR|DIFF sub sub ')' | leaf
leaf     = <index> RANGE <min> <max> | <index> EQ <value>

配套 verb:VIEW.LIST(目录 + 模式 + 形状)、VIEW.EXPLAIN name(带逐叶子基数的树)、VIEW.VERIFY name(成员数 / 字节数 / 排序排除数——字节数和排序排除数只对 materialized 有意义;virtual 视图什么都不存,两项都报 0,而校验一个 virtual 视图要付一次全新的 eval_tree,这是 virtual 视图唯一比较贵的地方)、VIEW.REBUILD name(强制重建 materialized 内容——保持答案不变)、VIEW.DROP name

三条结构规则

  1. 组件是具名索引。 叶子携带一个形状(RANGE min maxEQ v,在 CREATE 时强制转换成被引用索引的类型);视图层自己不持有任何谓词。树深 ≤ 3、叶子 ≤ 4 个;AND / OR 可能被引擎重排,DIFF 固定是左减右。
  2. 视图只存成员关系和顺序——绝不存字段值。ORDER BY <index> 提供排序键;不在排序索引里的行被排除(有计数,VIEW.VERIFY 可见)。
  3. 补水是解引用,不是查询。 VIA <template>(例如 user:{key.1}{key} = 成员键,{key.N} = 它按 : 切分后的第 N 段)把每个成员映射到一个目标键;VIEW.QUERY … FIELDS f… 在目标所属的 shard 上、通过第二次内部扇出读取那些字段。目标不存在 = 行字段为 nil。目标不接受任何谓词。

模式与成本模型

怎么选:高写入域上的一个看板 top-100 想要 MODE materialized TOPK 100(内存有界,非争夺者被 O(1) 拒绝);一个中等规模域上偶尔翻页的报表想要 MODE virtual(零写入税,永远新鲜)。VIEW.EXPLAIN 给出逐叶子的基数——那就是这次判断所需的选择率数据。

DIFF:声明式的反连接

DIFF 是左减右,且不满足交换律——它是引擎唯一不会重排其子节点的树节点。经典用法是“合格但未被排除”:

VIEW.CREATE assignable
    QUERY ( DIFF j_pri RANGE 0 100 j_state EQ quarantined )
    ORDER BY j_pri DESC
    MODE virtual

——工作优先级带里的每一个 job,减去被隔离的那些。

用 SQL 的话说,这就是 WHERE … AND NOT EXISTS (…) 那个形状——只不过做成了一条声明过的访问路径,而不是每次查询时的子查询(rds-workloads.md 映射了这套词汇的其余部分)。

一致性

与索引同一个信封(indexes.md):与触发它的那次写入在所属 shard 内原子,跨 shard 归并时没有全局快照(SCAN 类)。

Embedded

类型化 API,在 index 这个 cargo feature 后面(默认开启)——树作为一个值传进去,进程内没有文本语法,而且每个调用都返回 KevyResult(4.0 的单一错误货币):

use kevy_embedded::{
    Config, IndexKind, IndexValType, IndexValue, Store, ViewLeaf,
    ViewMode, ViewTree,
};

fn main() -> kevy_embedded::KevyResult<()> {
    let store = Store::open(Config::default())?;
    store.idx_create(b"j_pri", b"job:", b"pri", IndexValType::I64,
                     IndexKind::Range)?;
    store.idx_create(b"j_state", b"job:", b"state", IndexValType::Str,
                     IndexKind::Range)?;

    let tree = ViewTree::And(
        Box::new(ViewTree::Leaf(ViewLeaf {
            index: b"j_pri".to_vec(),
            min: IndexValue::I64(0),
            max: IndexValue::I64(100),
        })),
        Box::new(ViewTree::Leaf(ViewLeaf {
            index: b"j_state".to_vec(),
            min: IndexValue::Str(b"ready".to_vec()),
            max: IndexValue::Str(b"ready".to_vec()),   // EQ = same min/max
        })),
    );
    store.view_create(b"ready_jobs", tree, b"j_pri", /*desc*/ true,
                      ViewMode::Materialized { top_k: 100 })?;

    store.hset(b"job:1", &[
        (b"pri".as_slice(), b"10".as_slice()),
        (b"state".as_slice(), b"ready".as_slice()),
    ])?;

    // One page: (Vec<(key, order_value)>, Option<resume_cursor>).
    let (rows, _next) = store.view_query(b"ready_jobs", None, 10)?;
    for (key, val) in &rows {
        println!("{} {val:?}", String::from_utf8_lossy(key));
    }
    Ok(())
}

性能

实测信封——钳制住在 bench/viewgate.sh 里,对着一台真服务器在 100 万行上跑:

逐成员内存 ≈ order_value_width + key_len + 48 字节,由 VIEW.VERIFY 实时报告。

另见