RDS→kevy 建模 cookbook
你正在把一个关系型数据模型搬上 kevy。下面每个 recipe 用的都是已交付的原语——没有 roadmap 功能,没有「即将推出」。每一条都点名它替代的 RDS 概念,和承接它的 kevy 模式。
所有 recipe 背后的设计立场是:建模访问路径,而不是 schema。RDS 允许你把这个决定推迟给查询规划器;kevy 要你把它说出来——回报是服务时的微秒级页面(实测数字见 bench/VALIDATION-LEDGER.md)。
每个命令块都能对一台新起的本地 kevy 原样运行(kevy --port 6004;recipe 11–14、16 和 20 还需要 kevy.toml 里 [feed] enabled = true——见 docs/cdc.md)。bench/cookbook_smoke.sh 会把(英文版)cookbook 里的每一行 kevy-cli 对一台一次性服务器执行一遍,保证这些命令块永远诚实。
1. 表与行
SQL 对应物:CREATE TABLE + SELECT col FROM t WHERE id = ?——矩阵:表、行、列。
一行就是一个带类型前缀的 hash:
kevy-cli -p 6004 HSET user:42 name ada email ada@example.com age 36
kevy-cli -p 6004 HGET user:42 name
kevy-cli -p 6004 HGET user:42 phone # NULL = 字段缺席:天然回答 (nil)- 表 → key 前缀(
user:)。列 → hash 字段。主键 → key 本身。 - NULL = 字段缺席。不要存哨兵字符串;
HGET缺失字段天然回答 nil,索引规格把缺失字段视为「该行被排除」(在IDX.VERIFY计数里可见)。 - 列类型由你定:kevy 存的是字节。在类型真正要紧的地方声明它——创建索引时(
TYPE i64|f64|str|vector);强转失败会被计数,绝不静默入索引。
2. 一对多、多对多
SQL 对应物:外键列 + 关联表;SELECT … FROM orders WHERE user_id = ?——矩阵:JOIN。
关系由链接 key 承载,每一侧一个 set:
kevy-cli -p 6004 HSET order:1001 user_id 42 total 1999 status shipped
kevy-cli -p 6004 HSET order:1002 user_id 42 total 550 status pending
kevy-cli -p 6004 SADD user:42:orders 1001 1002 # 1-N:成员 = 订单 id
kevy-cli -p 6004 RPUSH order:1001:items sku-7 sku-9
kevy-cli -p 6004 SADD tag:urgent:orders 1001 # N-M:每侧一个 set
kevy-cli -p 6004 SADD order:1001:tags urgent或者干脆跳过链接 key:把外键放进行里(上面的 user_id),声明一个索引——IDX.QUERY … EQ 42 就是这个世界的 SELECT … WHERE user_id = 42,一跳完成 hydrate:
kevy-cli -p 6004 IDX.CREATE order_user ON PREFIX order: FIELD user_id TYPE i64 KIND range
kevy-cli -p 6004 IDX.QUERY order_user EQ 42 FIELDS total status3. 序列
SQL 对应物:AUTO_INCREMENT / CREATE SEQUENCE + nextval()——矩阵:PRIMARY KEY、UNIQUE、AUTO_INCREMENT。
kevy-cli -p 6004 INCR seq:order # 取一个 id
kevy-cli -p 6004 INCRBY seq:order 100 # 块分配:一次发 100 个 id
# 在应用内存里派发,用完再取块分配是高吞吐形态;崩溃留缝的契约与 PostgreSQL 序列一致。
4. 乐观锁(行版本)
SQL 对应物:UPDATE t SET …, version = v+1 WHERE id = ? AND version = v(版本列 CAS)——矩阵:事务。
服务器侧:WATCH/MULTI——CAS 循环。事务是连接作用域的,所以要在一个 REPL 会话里跑(这里用 heredoc 喂入):
kevy-cli -p 6004 HSET user:42 balance 100 version 7
kevy-cli -p 6004 <<'TXN'
WATCH user:42
HGET user:42 version
MULTI
HSET user:42 balance 90 version 8
EXEC
TXNWATCH 之后有人动过 user:42 的话,EXEC 回答 nil——竞争输了;重读重试。
Embedded 侧:把「读—判断—写」放进一个 atomic() 块——shard 锁让这个分支天然无竞争,不需要重试循环。
5. CHECK 约束与多 key 不变量
SQL 对应物:CHECK (balance >= 0) + 触发器维护的审计行——矩阵:约束与触发器。
RDS 在引擎里跑 CHECK (balance >= 0)。kevy 的替代是原子块内的读:应用评估不变量,引擎保证判断与写提交在一起。
// embedded——不许透支的扣款,附带一条审计行:
store.atomic(b"acct:7", |ctx| {
let bal: i64 = parse(ctx.hget(b"acct:7", b"balance")?);
if bal < amount { return Err(Overdraw); }
ctx.hset(b"acct:7", &[(b"balance", &(bal - amount))])?;
ctx.rpush(b"acct:7:ledger", &[entry])?;
Ok(())
})跨 shard 不变量:atomic_all_shards(确定性锁序,文档化的死锁豁免)。慎用——它是可串行化事务的大锤,而多数不变量按设计就住在一个 key 前缀底下。
6. 幂等键
SQL 对应物:UNIQUE INDEX + INSERT … ON CONFLICT DO NOTHING——矩阵:PRIMARY KEY、UNIQUE、AUTO_INCREMENT。
kevy-cli -p 6004 HSET req:9001 idem_key pay-2026-07-04-a77 amount 1999
kevy-cli -p 6004 IDX.CREATE req_idem ON PREFIX req: FIELD idem_key TYPE str KIND unique
kevy-cli -p 6004 IDX.QUERY req_idem EQ pay-2026-07-04-a77 # 重复以多命中读的形式可见
kevy-cli -p 6004 IDX.VERIFY req_idem # ……并在这里被计数
kevy-cli -p 6004 SET idem:pay-2026-07-04-a77 1 NX PX 86400000先写行、再查询——重复是*可见的*(unique kind 在 VERIFY 里计数而不是拒绝写入;这是声明式栅栏,不是写闸门)。要硬闸门,就在处理之前用 SET … NX PX 形态:NX 是原子认领,TTL 是保留窗口。
7. 软删除
SQL 对应物:deleted 标记列 + 部分索引 / 视图 WHERE deleted = 0——矩阵:VIEW。
打标记,不移除:
kevy-cli -p 6004 HSET user:42 deleted 0 age 36
kevy-cli -p 6004 HSET user:43 deleted 1 age 51
kevy-cli -p 6004 IDX.CREATE user_live ON PREFIX user: FIELD deleted TYPE i64 KIND range
kevy-cli -p 6004 IDX.QUERY user_live EQ 0 LIMIT 100 # 只要活的行视图把过滤条件永久组合掉——调用方再也不用复述它:
kevy-cli -p 6004 IDX.CREATE user_age ON PREFIX user: FIELD age TYPE i64 KIND range
kevy-cli -p 6004 VIEW.CREATE live_users QUERY '(' AND user_live EQ 0 user_age RANGE 18 200 ')' ORDER BY user_age
kevy-cli -p 6004 VIEW.QUERY live_users LIMIT 108. 复合排序(ORDER BY a, b)
SQL 对应物:复合索引上的 ORDER BY a, b——矩阵:ORDER BY / LIMIT / OFFSET。
写入时把复合序编码进一个被索引的 score 字段:有界整数 b 用 score = a * 1_000_000 + b,字典序复合用零填充的字符串字段——一个索引、一个 ORDER BY;写钩子像维护普通字段一样维护它:
kevy-cli -p 6004 HSET evt:1 ord '2026-07-04|000042'
kevy-cli -p 6004 HSET evt:2 ord '2026-07-04|000007'
kevy-cli -p 6004 HSET evt:3 ord '2026-07-05|000001'
kevy-cli -p 6004 IDX.CREATE evt_ord ON PREFIX evt: FIELD ord TYPE str KIND range
kevy-cli -p 6004 IDX.QUERY evt_ord RANGE '2026-07-04|000000' '2026-07-04|999999' LIMIT 1009. JSONB
SQL 对应物:JSON/JSONB 列 + 生成列索引——矩阵:类型系统。
拍平成 hash 字段:profile.city → 字段 profile.city。你保住了按字段读写、字段级 TTL(HEXPIRE)和可索引性——JSONB 给你的一切,除了 JSON-path 查询,那是永久出局的(查询引擎斜坡;见 designing-on-kevy.md 的 REFUSED 表)。
kevy-cli -p 6004 HSET user:7 profile.city tokyo profile.plan pro
kevy-cli -p 6004 HGET user:7 profile.city
kevy-cli -p 6004 HEXPIRE user:7 3600 FIELDS 1 profile.plan # 字段级 TTL 在拍平后依然可用没人索引的深嵌套 blob 可以留成一个序列化字段;某条路径一旦要紧,就把它提升成字段。
10. 级联删除 / 外键
SQL 对应物:FOREIGN KEY … ON DELETE CASCADE——矩阵:约束与触发器。
级联是应用模式,从来不是引擎魔法:
- 同步、小爆炸半径:在一个原子块内删除(
ctx.del(row)、ctx.srem(parent_link, id))。 - 批量 / 前缀形状:
delete-prefix——限速、可续传。 - 异步:CDC 消费者(带
PREFIX的FEED.READ)响应父行删除并清理子行——触发器的替代品,提交后、解耦、可重放。
kevy-cli -p 6004 HSET order:1001 user_id 42
kevy-cli -p 6004 RPUSH order:1001:items sku-7 sku-9
kevy-cli -p 6004 SADD order:1001:tags urgent
kevy-cli delete-prefix -p 6004 --rate 5000 order:1001: # 子行清空,父行还在11. 你不需要的 outbox
SQL 对应物:事务性 outbox 表 + 中继 worker——矩阵:CDC。
事务性 outbox 模式之所以存在,是因为 RDS 提交和消息总线发布无法原子化。在 kevy 里feed 就是 outbox:每笔已提交的写入本来就是一个位于 (generation, offset) 游标处的变更帧,at-least-once、可按前缀过滤(docs/cdc.md)。消费 FEED.READ;别再造第二本日志。
# 需要 kevy.toml 里 [feed] enabled = true(见 ../cdc.md)
kevy-cli -p 6004 HSET order:9001 status paid
kevy-cli -p 6004 FEED.SHARDS
kevy-cli -p 6004 FEED.TAIL 0 # 新消费者的起始游标
kevy-cli -p 6004 FEED.READ 0 1 0 COUNT 10 PREFIX order: # gen 1 = 新数据目录的第一个 generation12. 审计历史
SQL 对应物:触发器维护的审计/历史表(或 binlog 考古)——矩阵:CDC。
CDC 的保留窗口就是审计日志:帧按提交顺序携带已应用效果的 argv。按你欠合规的窗口给 feed backlog 定容量,用游标消费者导出到冷存储。要做时间点重建:恢复快照 + 重放到 (gen, offset) 恢复点(persistence.md)。
kevy-cli -p 6004 HSET acct:7 balance 100
kevy-cli -p 6004 HSET acct:7 balance 90
kevy-cli -p 6004 FEED.READ 0 1 0 COUNT 100 PREFIX acct: # 谁在什么时候设了什么,按提交顺序13. 回滚窗口(反向镜像)
SQL 对应物:切换期间反向复制回旧主库——迁移 playbook 阶段 5。
切换期间,跑一个把 kevy 写入镜像回旧 RDS 的 CDC 消费者(FEED.READ → UPDATE 语句)。这样你的回滚方案是「把应用指回去」,不是「反向迁移数据」。信心固化后退役镜像;kevy-cli diff(按前缀摘要)就是信心仪表。
kevy-cli -p 6004 HSET user:42 name ada
kevy-cli -p 6004 FEED.READ 0 1 0 COUNT 10 PREFIX user: # 镜像消费者的读循环
kevy-cli diff 127.0.0.1:6004 127.0.0.1:6004 user: # 摘要一致:安全形态的自检
kevy-cli diff old-rds-mirror.internal:6379 127.0.0.1:6004 user: # needs-external14. 分析导出
SQL 对应物:喂数仓的 ETL 作业 / binlog tap——矩阵:CDC。
服务与分析不共用一个引擎。导出模式:
export——逻辑导出、可续传、RESP 走到哪就能载到哪。- CDC → 数仓:游标消费者把插入流进你的 OLAP 存储,正是 CDC-to-Kafka 的形状。
- 只读 listener(embedded-listener.md)供 embedded 应用做临时抽取。
kevy-cli -p 6004 HSET order:1001 user_id 42 total 1999
kevy-cli export -p 6004 --prefix order: /tmp/orders.resp
kevy-cli -p 6004 FEED.READ 0 1 0 COUNT 100 PREFIX order: # CDC 到数仓的读循环15. 载入顺序(延迟索引规则)
SQL 对应物:先 LOAD DATA、后 CREATE INDEX(批量载入纪律)——矩阵:二级索引 DDL。
先批量载入,后声明索引/视图:回填以约 7s/百万行的速度从既有行构建——比给每条导入行付写钩子便宜几个数量级(migration.md)。
kevy-cli -p 6004 HSET item:1 price 10
kevy-cli -p 6004 HSET item:2 price 25
kevy-cli -p 6004 HSET item:3 price 7
kevy-cli export -p 6004 --prefix item: /tmp/items.resp
kevy-cli import -p 6004 /tmp/items.resp # 先批量载入:不付索引写钩子
kevy-cli -p 6004 IDX.CREATE item_price ON PREFIX item: FIELD price TYPE i64 KIND range # 后声明:走回填
kevy-cli -p 6004 IDX.QUERY item_price RANGE 0 100 LIMIT 10后面三个 recipe 换了负载:不再是被替换的 RDS,而是 AI agent 的记忆栈。不需要任何新东西——会话状态、情景记忆和 RAG 检索,就是同一批访问路径模式换了 key 前缀。
16. 带 TTL 的会话上下文
SQL 对应物:sessions 表 + 过期 cron——矩阵:容量估算与运维差异。
agent 的工作上下文是一行带租约的数据:压缩后的对话住在 hash 里,EXPIRE 是闲置逐出策略(每轮续租——滑动窗口),feed 是审计轨迹——有人问「agent 在第 7 轮知道什么」时拿来重放。
# 需要 kevy.toml 里 [feed] enabled = true(见 ../cdc.md)
kevy-cli -p 6004 HSET session:a7 user 42 turns 6 messages 'wants refund for order 1001; tone calm' last_tool order_lookup
kevy-cli -p 6004 EXPIRE session:a7 3600
kevy-cli -p 6004 HSET session:a7 turns 7 messages 'refund approved; awaiting confirmation'
kevy-cli -p 6004 EXPIRE session:a7 3600 # 每一轮都续租
kevy-cli -p 6004 FEED.TAIL 0 # 审计游标:日志现在的尾部
kevy-cli -p 6004 FEED.READ 0 1 0 COUNT 100 PREFIX session: # gen 1 = 新数据目录的第一个 generationmessages 字段装的是你的压缩步骤产出的任何摘要;重写它就是一条 HSET,而每次修订本来就是提交序里的一个变更帧——多数 agent 框架外挂的「对话历史」表,就是 recipe 12 的审计日志白送给你。
17. 情景记忆(时间 × 语义)
SQL 对应物:WHERE ts BETWEEN … + pgvector ORDER BY embedding <=> ? LIMIT k——矩阵:SELECT。
情景记忆对同一批行回答两个问题:最近发生了什么(时间)和*什么与此相似*(语义)。一个前缀,每个问题一个索引——DIM 8 是为了演示可读;真实 embedding 是 768+ 维、以 f32-LE blob 传输,下面的 csv: 调试形态在任何接受向量的地方都可用(存储字段与查询向量走同一个解析器——vector-search.md)。
kevy-cli -p 6004 HSET mem:1 ts 1783200000 kind obs what 'user prefers dark roast' v csv:0.9,0.1,0,0,0,0,0,0
kevy-cli -p 6004 HSET mem:2 ts 1783203600 kind obs what 'user asked about decaf' v csv:0.8,0.3,0.1,0,0,0,0,0
kevy-cli -p 6004 HSET mem:3 ts 1783207200 kind reflection what 'coffee questions cluster in the morning' v csv:0,0.2,0.9,0.1,0,0,0,0
kevy-cli -p 6004 IDX.CREATE mem_ts ON PREFIX mem: FIELD ts TYPE i64 KIND range
kevy-cli -p 6004 IDX.CREATE mem_kind ON PREFIX mem: FIELD kind TYPE str KIND range
kevy-cli -p 6004 IDX.CREATE mem_ann ON PREFIX mem: FIELD v TYPE vector KIND ann DIM 8
kevy-cli -p 6004 IDX.QUERY mem_ts RANGE 1783203000 1783210000 LIMIT 10 FIELDS what # 最近的记忆
kevy-cli -p 6004 IDX.QUERY mem_ann KNN csv:0.85,0.2,0,0,0,0,0,0 LIMIT 2 FIELDS what ts # 相似的记忆
kevy-cli -p 6004 IDX.QUERY COMPOSE AND mem_ts RANGE 1783203000 1783210000 mem_kind EQ reflection LIMIT 10 FIELDS whatCOMPOSE AND 合取标量腿(RANGE/EQ)——这里是「在这个时间窗内 AND 是一条 reflection」。至于*窗口内的相似*,有意不提供 KNN 腿(在图遍历里做过滤是查询引擎斜坡,REFUSED):给 LIMIT 留余量跑 KNN,像上面那样用 FIELDS hydrate ts,窗口外的命中在客户端丢弃。
18. 带混合检索的 RAG 分块
SQL 对应物:tsvector 全文 + pgvector KNN,应用侧融合——矩阵:SELECT。
chunk 是同时携带两个检索面的行——文本和它的 embedding——一次写同时维护两个索引:
kevy-cli -p 6004 HSET chunk:1 doc kevy-guide seq 1 body 'rows are hashes under a typed key prefix' v csv:1,0,0,0,0,0,0,0
kevy-cli -p 6004 HSET chunk:2 doc kevy-guide seq 2 body 'indexes are declared once and maintained by the write hook' v csv:0,1,0,0,0,0,0,0
kevy-cli -p 6004 HSET chunk:3 doc kevy-guide seq 3 body 'the feed streams every committed write as a change frame' v csv:0,0,1,0,0,0,0,0
kevy-cli -p 6004 IDX.CREATE chunk_text ON PREFIX chunk: FIELD body TYPE str KIND text
kevy-cli -p 6004 IDX.CREATE chunk_ann ON PREFIX chunk: FIELD v TYPE vector KIND ann DIM 8
kevy-cli -p 6004 IDX.QUERY HYBRID chunk_text MATCH 'typed key prefix' chunk_ann KNN csv:0.9,0.1,0.1,0,0,0,0,0 LIMIT 2 FIELDS body
kevy-cli -p 6004 IDX.QUERY HYBRID chunk_text MATCH 'change frame' chunk_ann KNN csv:0,0.1,0.9,0,0,0,0,0 LIMIT 2 RRFK 20 FIELDS bodyHYBRID 在服务端跑两条腿并以倒数排名融合(RRF)合并:每个 key 在 BM25 列表与 KNN 列表上各取 Σ 1/(k + rank)——只看排名,所以两种异质分数尺度永远不需要归一化;同时在*两条*腿都靠前的 chunk,胜过只霸一条腿的 chunk。RRFK 就是那个 k(默认 60):信得过每条腿的头部命中、想让「两边都同意」占主导时调低;想把融合拉平、让共识深入两个列表时调高。
最后两个 recipe 彻底离开机架:边缘节点上的 kevy——同一个服务器二进制,或裁到 core 档 655 KB 的 kevy-embedded(iot.md)——说的还是同一套 verb,模式从数据中心到传感器网关逐字迁移。
19. 传感器缓存(最新值 + 存活租约)
SQL 对应物:readings_latest upsert 表 + 陈旧度 cron——矩阵:容量估算与运维差异。
每个传感器的当前值是一行数据;TTL 是存活契约。停止上报的传感器会从缓存中过期消失——缺席本身就是掉线信号,不用写清扫任务:
kevy-cli -p 6004 HSET sensor:t1 val 21.5 unit C ts 1783200000
kevy-cli -p 6004 EXPIRE sensor:t1 90
kevy-cli -p 6004 HSET sensor:t1 val 21.7 unit C ts 1783200030
kevy-cli -p 6004 EXPIRE sensor:t1 90 # 每次上报都续租
kevy-cli -p 6004 EXISTS sensor:t1 # 1 = 在报,0 = 已失联租约按你的告警容忍度定(这里 90 s = 连丢三个 30 秒上报)。要*响应*传感器失联而不是轮询,就开启带 x(expired)类的 keyspace 通知并订阅过期事件——同一契约的推送形态(pubsub.md)。
最近窗口是一条带硬上限的 stream——MAXLEN ~ 让节点内存无论跑多久都有界,这在动辄数月不重启的边缘盒子上正是要紧的不变量:
kevy-cli -p 6004 XADD sensor:t1:log MAXLEN '~' 1000 '*' val 21.5
kevy-cli -p 6004 XADD sensor:t1:log MAXLEN '~' 1000 '*' val 21.7
kevy-cli -p 6004 XLEN sensor:t1:log
kevy-cli -p 6004 XRANGE sensor:t1:log - + COUNT 10Embedded 形态:同一批 verb 走网关进程内的类型化 API——store.hset(…) / store.expire(…) / store.xadd(…)——完全不经过 socket;core feature 档承载本 recipe 用到的一切(iot.md)。
20. 边缘聚合(写时 GROUP BY + 上行)
SQL 对应物:每次仪表盘刷新重跑一遍的 SELECT zone, COUNT(*), SUM(w) … GROUP BY zone——矩阵:GROUP BY 与聚合。
边缘节点就地汇总、只上传摘要——原始读数多到没法上行。聚合声明一次,由写路径维护,于是「聚合作业」直接不复存在:
kevy-cli -p 6004 HSET reading:1 zone floor1 w 120
kevy-cli -p 6004 HSET reading:2 zone floor1 w 180
kevy-cli -p 6004 HSET reading:3 zone floor2 w 95
kevy-cli -p 6004 IDX.CREATE zone_w ON PREFIX reading: FIELD w TYPE i64 KIND agg GROUPBY zone
kevy-cli -p 6004 IDX.QUERY zone_w GROUP floor1 # [count, sum, min, max, avg]
kevy-cli -p 6004 IDX.QUERY zone_w GROUPS BY sum LIMIT 10 # 按负载排名的 zone上行就是 recipe 11 的 outbox 穿上工装:feed 本来就按提交顺序记着每笔写入,所以云同步消费者就是一个游标循环,链路断几个小时也能续——at-least-once、按提交序、按前缀过滤出云端要的那部分:
# 需要 kevy.toml 里 [feed] enabled = true(见 ../cdc.md)
kevy-cli -p 6004 FEED.TAIL 0
kevy-cli -p 6004 FEED.READ 0 1 0 COUNT 100 PREFIX reading: # 上行循环再配上 recipe 19 的 MAXLEN 上限和 TTL:原始读数在节点上有界,聚合行始终很小,feed 游标跨重启存活——除了 kevy 本身,整个边缘故事零活动件。
Recipe 索引
Recipe ↔ 它替代的 SQL 构造 ↔ 陈述语义与边界的 rds-workloads.md 矩阵行。
| # | Recipe | SQL 构造 | 矩阵行 |
|---|---|---|---|
| 1 | 表与行 | CREATE TABLE、点查 SELECT | 表、行、列 |
| 2 | 一对多、多对多 | 外键列、关联表、WHERE fk = ? | JOIN |
| 3 | 序列 | AUTO_INCREMENT / nextval() | PK、UNIQUE、AUTO_INCREMENT |
| 4 | 乐观锁 | 版本列 CAS UPDATE | 事务 |
| 5 | CHECK 约束 | CHECK (…) + 审计触发器 | 约束与触发器 |
| 6 | 幂等键 | UNIQUE INDEX + ON CONFLICT DO NOTHING | PK、UNIQUE、AUTO_INCREMENT |
| 7 | 软删除 | 标记列 + 过滤视图 | VIEW |
| 8 | 复合排序 | ORDER BY a, b | ORDER BY / LIMIT / OFFSET |
| 9 | JSONB | JSON 列 + 生成列索引 | 类型系统 |
| 10 | 级联删除 / 外键 | ON DELETE CASCADE | 约束与触发器 |
| 11 | 你不需要的 outbox | 事务性 outbox 表 | CDC |
| 12 | 审计历史 | 审计表 / binlog 考古 | CDC |
| 13 | 回滚窗口 | 切换期反向复制 | 迁移 playbook |
| 14 | 分析导出 | ETL / binlog tap 到数仓 | CDC |
| 15 | 载入顺序 | 先批量 LOAD DATA、后建索引 | 二级索引 DDL |
| 16 | 带 TTL 的会话上下文 | sessions 表 + 过期 cron | 容量估算与运维差异 |
| 17 | 情景记忆 | 时间 BETWEEN + pgvector KNN | SELECT |
| 18 | RAG 混合检索 | tsvector + pgvector,融合 | SELECT |
| 19 | 传感器缓存 | upsert 表 + 陈旧度 cron | 容量估算与运维差异 |
| 20 | 边缘聚合 | 每刷新一遍 GROUP BY + ETL 上行 | GROUP BY 与聚合 |