Structures 导览——6 子族速查 + KV 组合
给谁看:用结构层组合存储模型的人(KV / WAL / 队列 / 时序形态的构建者)。 本篇定位:导览页——6 子族速查 + KV 组合范式 + 陷阱清单。每种结构的详细文档(Settings 全参数 / API 详表 / 自定义注入工厂 / 持久化机制 / 恢复协议 / 反模式 / 指路)已拆为独立文档,下表"详细文档"列。
机制细节(帧格式 / 水位全表 / 协议内部状态机)从源码与设计稿查阅;meta 统一协议见
meta.md;性能数字见perf/structures-perf-baseline.md。
0. 6 子族速查(详细文档一览)
| 子族 | 详细文档 | 主类型 | 是什么 | 何时用 |
|---|---|---|---|---|
| Ring | ring.md |
BlittableRing<TKey> |
追加式 KV 环:页池 + 单调逻辑地址 + RCU 原地更新 + 环形驱逐 | 主真相源——KV 数据本体;高并发写/读 |
| Log | log.md |
EntryLog / DeltaLog |
WAL 页帧流:组提交 + 恢复水位 | 纯顺序日志;checkpoint delta(DeltaLog 形态) |
| Index 探测族 | index.md |
HashIndex<TKey> |
条目=地址+tag,不物化 key,判等经 KeyResolver 回读 | 点查为主、内存最省(实测 161 ns) |
| Index 比较族 | index.md |
BTreeIndex<TKey> / SkipListIndex<TKey> |
条目物化 (key, 地址),有序遍历/range scan/前缀批量删 | 需要排序/范围查询;SkipList 适合高写并发小记录(无分裂重排) |
| Metadata | versioned-metadata.md |
VersionedMetadata |
版本链追加式单值元数据,N=2 轮转回收 | 跨重启单值状态(水位/配置/锚点);meta 外部隔离传输 |
| Mirror | mirror.md |
WholeMirror / PagedMirror |
字节镜像:完整状态基线的版本链形态 | 整体快照外存(流式)/ 分页增量镜像 |
| Snapshot | snapshot.md |
StreamSnapshot / IncrementalSnapshot |
完整状态基线的纯流式形态(按字节截断)/ 段=帧的增量快照(段表 O(1) 恢复 + 阈值合并) | 流式帧文件快照 / raft 日志压缩(TierWAL 内部) |
主结构 2 类(Ring / Log);搭配件 4 类(Index 一族两实现 / Metadata / Mirror / Snapshot)—— 存在性=优化非正确性(搭配件经桥单向依赖、可摘;不配也安全,走全量重放 fail-safe)。
1. 快速上手:组合 KV(三步,可运行)
1.1 声明封闭形态([RingKey] 源生成器,一行)
开放泛型(BlittableRing<TKey>/HashIndex<TKey> 等)的构造是 protected internal——消费面只经
[RingKey] 生成的封闭薄类(编译期封闭 + CreateAsync 工厂)。内置 long 已声明(TC.Tier.Runtime
程序集),RingOfLong/HashOfLong/BTreeOfLong/SkipListOfLong 开箱即用;自定义 Key 在消费程序集
加一行:
[assembly: RingKey(typeof(OrderId))] // 产出 RingOfOrderId / HashOfOrderId / ...
⚠️ Mirror 子族无工厂——直接
new WholeMirror(...)+Initialize+WaitForReady三步。 详见mirror.md§3。
1.2 装配与写读(真实签名)
using TC.Tier.Core.IO;
using TC.Tier.Runtime.Storage;
using TC.Tier.Runtime.Structures.Ring;
using TC.Tier.Runtime.Structures.ProbingIndex;
using TC.Tier.Contracts.Structures;
// ① 文件系统 + 配置(默认值即生产值;示例用小型页池便于观察)
using var fs = TierFs.New("memory:");
var ringSettings = new BlittableRingSettings(new StorageEngineOptions("kv-ring", 64L << 20))
{
PageSize = 4096, MemorySize = 64 * 1024, MutableFraction = 0.5,
};
var hashSettings = new HashIndexSettings(new StorageEngineOptions("kv-hash", 64L << 20));
// ② 封闭类型 CreateAsync 一步就绪(= 构造 + Initialize + WaitForReadyAsync)
await using var ring = await RingOfLong.CreateAsync(ringSettings, fs); // 真相源:record 流
await using var index = await HashOfLong.CreateAsync(fs, hashSettings, ring); // 派生数据:判等闭环必注入 ring
// ③ 写读编排(正序两步写 / 两段合口径读)
static LogicalAddress KvPut(RingOfLong ring, IIndex<long> index, long key, ReadOnlySpan<byte> value)
{
var addr = ring.Write(key, value); // ① 真相源先写,得地址
index.Insert(key, addr, LogicalAddress.Empty); // ② 索引后插(指向该地址)
return addr;
}
static bool KvTryGet(RingOfLong ring, IIndex<long> index, long key, Span<byte> buf, out int len)
{
len = 0;
var addr = index.Find(key); // ① 索引命中得地址
if (addr == LogicalAddress.Empty) return false;
len = ring.GetValue(addr, buf); // ② 真相源按址取值
return true;
}
终态读形态(批量最快):index.EnterScope() 一批一进出 +
IndexScope.Find + Ring.GetValueSpan(零拷贝切片;Ring 读为内容自愈读——判据+magic/CRC 校验
内聚,调用方零保护零感知;溢出 record 回退 thread-static 拷贝;span 仅同步栈内合法)。
批口径实测反超 FASTER(perf 文档 §3)。
1.3 恢复(两段式协议,锚点 W)
① Ring.CreateAsync → 恢复水位;锚点 W = ring.ReadOpaqueMeta()
(无锚点 / 损坏 / W 越过当前尾 → 回退 BeginAddress——宁可旧多重放)
② index 拉流重放:ScanAsync(W, TailAddress) 逐条 Insert 自建
(Hash/SortedIndex 有主存储帧时优先载帧,见 index.md §6/§7)
锚点 W = 索引上次主存储 dump/重放完成的水位,经 Ring 的 opaque 随水位线原子落盘;每次
dump/重放后 ring.SetOpaqueMeta(W) 即可。恢复协议全文见 meta.md。
2. 概念(术语在此定义)
- 2 主结构 + 4 搭配件 = 产品发生器:Ring(数据真相源)/ Log(操作流)是主结构; 索引(一族两实现)/元数据/镜像/快照是搭配件(经桥单向依赖、可摘——存在性=优化非正确性)。
- 真相源 vs 派生数据:Ring 是数据真相(record 流),索引是派生数据(可重建)。 写编排正序:先真相源写、再索引插;反向 = 索引指向不存在的数据。
- 地址一等公民:16B
LogicalAddress是唯一的取值句柄——先经索引发现地址,之后永久直达取值 (跳过 hash);判等/哈希只比较地址本身。 - 恢复统一模型:载基线(快照/镜像,到 W)+ 重放(W, 尾]。快照按字节截断(纯流式), 镜像按版本号截断(版本链 N=2/回跳/回退)——同族异坐标系。
- 术语:WAL = 写前日志(Log 即 WAL);RCU = 读-拷贝-更新(Ring 原地更新语义); opaque = 外部记录随水位线原子落盘的扩展槽(meta.md §4);2PC 六件套 = Prepare/ConfirmCommitted/ OnCommitted/Abort 的事务参与协议(见各子族文档 §4 / §5);帧 = 持久化格式的三段式单元 (头先行校验/体/尾 CRC)。
3. 怎么选(七种结构速查,详细决策见各子族文档)
| 结构 | 是什么 | 何时用 | 注意 |
|---|---|---|---|
Ring(BlittableRing<TKey>) |
追加式 KV 环:页池 + 单调逻辑地址 + 原地更新 + 环形驱逐 | 主真相源——KV 数据本体;高并发写/读 | 默认内存上限 16GB(MemorySize 旋钮);TKey : unmanaged;8 水位详见 ring.md §4.3 |
| HashIndex | 探测族:条目=地址+tag,不物化 key,判等经 KeyResolver 回读 | 点查为主、内存最省(实测 161 ns) | 构造必注入 KeyResolver(判等闭环);无有序遍历;自建主存储(PersistenceKind.Builtin) |
| BTreeIndex / SkipListIndex | 比较族:条目物化 (key, 地址),有序遍历/range scan/前缀批量删 | 需要排序/范围查询 | 内部操作闸读写全互斥(任意并发组合安全;游标=单步粒度);恢复重放需要 KeyResolver;自建主存储(PersistenceKind.Builtin) |
Log(EntryLog/DeltaLog) |
WAL 页帧流:组提交 + 恢复水位 | 纯顺序日志;checkpoint delta | 单写者;超单页抛(entry 不跨页);DeltaLog = 嵌入式 meta + 临时文件 |
Metadata(VersionedMetadata) |
版本链追加式单值元数据,N=2 轮转回收 | 跨重启单值状态(水位/配置/锚点);meta 外部隔离传输 | PayloadSize 须 ≥ 托管块上界;Sync/Async 持久化策略 |
Mirror(WholeMirror/PagedMirror) |
字节镜像:完整状态基线的版本链形态 | 整体快照外存(流式)/ 分页增量镜像 | 与 Snapshot 同族异坐标系(版本号 vs 字节截断);无 Create 工厂——new + Initialize + WaitForReady |
Snapshot(StreamSnapshot) |
完整状态基线的纯流式形态(按字节截断) | 流式帧文件快照 | 无版本概念;与 Ring 自带区间快照无引用关系 |
增量快照(IncrementalSnapshot) |
段 = 帧的增量快照:新段 append 不重写旧段 + 段表 O(1) 恢复 + 阈值合并 | raft 日志压缩(镜像快照第三部件——TierWAL 内部快照) | 段累积需合并(raft 只需最新快照);合并 = 低频全量重写 |
索引三选:点查 → Hash;有序遍历/range → BTree/SkipList;高写并发小记录 → SkipList(无分裂重排)。
性能对照见 perf/structures-perf-baseline.md §3。
4. 持久化机制(两族索引)
两族均用自建主存储(PersistenceKind.Builtin,可关 None)——基类是机制容器:
后台 dump 编排 / 版本链 N 版轮替 / 帧走链恢复载入 / 策略触发;子类只实现格式布局
(几何 + 桶区/溢出池逐槽拷贝 + 物化——铁律 10,对齐 LogBase/RingBase/MirrorBase)。
- HashIndex:链 + N 版轮替
- SortedIndex:固定锚点覆写
⚠️ 历史上索引族曾用
ITransferPersistence桥接注入(WholeMirrorPersistence托管 WholeMirror); 镜像通道注入面已退役——见ProbingIndexBase.cs头注释。WholeMirrorPersistence桥接器仍保留 在Runtime/DataMirror/给其他需"借镜像存储能力"的组件消费。详见index.md§6 与mirror.md§3.2。
5. 反模式(组合层必守——详见各子族文档 §8)
- 写编排反序:先
index.Insert再ring.Write——索引指向不存在的数据。 - 零拷贝 span 跨 await:
GetValueSpan返回值仅同步栈内合法——跨 await/跨线程消费必须走拷贝 API (TryGetValue/GetValue)。详见ring.md§8。 - Log 并发 Append:单写者硬性要求(多生产者经队列汇聚)。详见
log.md§8。 - 绕过
[RingKey]直接继承开放泛型——封闭薄类是唯一消费面(分析器诊断不满足 unmanaged 约束的声明)。 - 为索引手搭 checkpoint 引擎/存储——两族均走自建主存储(
PersistenceKind.Builtin);详见index.md§6。 - 给地址直达读加会话/索引——地址即句柄:
GetValue(addr)永久零税。 - 跨实例组合 DeleteOnClose=true——跨重启组合必须
DeleteOnClose=false(否则索引重建时真相源被清)。 - 增长后检查索引阈值——增长必须在插入之前检查(增长时刻表内条目必可经 KeyResolver 解析)。
- 混淆 Ring 区间快照与 StreamSnapshot——前者是 Ring 导出能力(
OpenSnapshotReader/Writer), 后者是独立文件形态。详见snapshot.md§1 / §8。 - 混淆 Mirror 与 Snapshot 截断坐标系——Mirror 按版本号截断(N=2 轮替);Snapshot 按字节截断
(纯流式)。详见
mirror.md§1 /snapshot.md§1。
6. 想深入?指路
| 想懂什么 | 去哪 |
|---|---|
| Ring 详细(8 水位 / Codec / KeyResolver / 自愈读 / 锚点 W 协议) | ring.md |
| Log 详细(EntryLog vs DeltaLog / 组提交策略 / 单写者 / Replay) | log.md |
| Index 详细(三选一决策树 / KeyResolver 闭环 / PersistenceKind / RecoveryHints) | index.md |
| VersionedMetadata 详细(版本链 N=2 / Sync/Async 持久化 / MetadataMetaTransport) | versioned-metadata.md |
| Mirror 详细(WholeMirror 三拍 / PagedMirror 逐页 / 2PC Abort / 跨实例恢复) | mirror.md |
| Snapshot 详细(StreamFrameWriter/Reader / 段表 O(1) 恢复 / 阈值合并 / raft 日志压缩) | snapshot.md |
| meta 统一协议(opaque 搭车/块格式/自定义传输) | meta.md |
| Session 协调协议(写/读/检查点三 op、悬挂裁决、故障模型) | session.md |
| 引擎使用(读写/水位/恢复/Compact) | storage-engine.md |
| 段表 / lease 协议 | segment-table.md / lease-protocol.md |
| 性能基线(点查/写/恢复/并发扩展) | perf/structures-perf-baseline.md / perf/log-perf-baseline.md / perf/kv-composition.md |
| 帧格式/主存储机制/恢复细节 | 源码注释(Structures/ 各 partial)与设计稿(docs/design/) |
机制级细节(8 水位全表、帧字节布局、dump 协议、2PC 内部状态机)按需从源码与设计稿查阅——使用面 不需要这些细节;能力全集以类型 XML 注释为准。