Mooncake Store 对象模型:对象、切片、副本和 Segment

梳理对象、切片、副本、Segment,以及 Master 与 Client 的职责,作为理解 Mooncake Store 读写流程的入口。

Code walkthroughsMooncake and HiCache internalsLLM servingKV cacheMooncakeDistributed systems

Mooncake Store 不是“Client 把值发给服务器,再由服务器保存”的中心化 KV 服务。它更像一个对象元数据控制面,再加上由 Client 和 Provider 执行的数据传输层。

读源码前先建立对象模型,否则 Put/Get 很容易看散。

主要入口:

text
mooncake-store/include/client_service.h
mooncake-store/include/types.h
mooncake-store/include/replica.h
mooncake-store/src/client_service.cpp
mooncake-store/src/master_service.cpp

layer view

Mooncake Store 的对象模型:控制面记录事实,数据传输层搬运内容

对象键是上层名称,切片描述本地缓冲区,副本才是落在 Segment 上的可读数据。Master 只维护这些关系,不参与大块数据传输。

调用方视角

上层看到对象键和本地缓冲区

对象键Slice(ptr, size)BatchPut / BatchGet

Master 元数据

事实表和可见性

对象元数据副本列表租约空间分配配额

副本位置

真正能读写的位置

Segment ID偏移量长度PROCESSINGCOMPLETE

Client 数据传输层

根据元数据生成传输任务

TransferSubmitter本地内存复制Transfer Engine热缓存

对象不只是一个值

Store 对外暴露对象键,但内部不会假设一个值必须位于连续内存。写入时,调用方传入的是 Slice 列表:

text
Slice
  ptr
  size

这对 KV 缓存很重要,因为上层数据往往已经按 K/V、层、注意力头和页拆开。Store 不应该为了让抽象看起来整齐,就强行把它们复制到一块连续的大缓冲区中。

因此对象模型更接近:

text
ObjectKey
  -> 对象元数据
  -> 切片
  -> 副本
       -> Segment ID/名称
       -> 偏移量
       -> 长度
       -> 状态

对象键是上层看到的名称;切片描述本次读写使用的本地缓冲区;副本则是 Store 内部真正可以读写的数据拷贝。

Segment 是可寻址存储空间

Segment 描述的是一段可被寻址的远端或本地存储空间。它包含:

text
Segment
  id
  name
  base
  size
  te_endpoint
  protocol

Store 里的副本不会直接指向“某台机器上的某个变量”,而是落在某个 Segment 的一段偏移和长度上。Transfer Engine 随后再把这段描述转换成 READ 或 WRITE 请求。

这层间接性很重要。Master 维护 Segment 和副本元数据,Client 根据元数据选择本地内存复制、Transfer Engine、NoF 或卸载路径。

副本状态决定数据是否可读

Replica 有状态。关键状态包括:

text
INITIALIZED
PROCESSING
COMPLETE
REMOVED
FAILED

一次 Put 过程中,Master 可以先分配副本,但这不等于数据已经写完。只有进入 COMPLETE 状态的副本才能成为正常读路径的候选。

因此,Store 的读取正确性不能简化为“对象键存在”,而要同时满足:

text
对象键存在
  + 元数据中有可用副本
  + 副本状态是 COMPLETE
  + 租约仍在有效期内
  + Client 能从对应 Segment 成功读出数据

这比普通 KV 服务复杂得多,但它允许大对象的数据传输绕开 Master。

Master 维护权威元数据

Master 的核心职责是维护权威元数据,而不是搬运数据。

它至少要回答:

  • 哪些 Client 和 Segment 已经挂载。
  • 每个对象有哪些副本。
  • 哪些副本正在写入。
  • 哪些副本已经进入 COMPLETE 状态。
  • 哪些 Segment 正在排空或卸载。
  • 租户配额、分配策略、本地热缓存和租约是否允许本次操作。

Put 时,Master 负责分配副本并推进状态;Get 时,Master 负责返回可读副本和租约。真正的数据内容不经过 Master。

Client 负责组织数据传输

Client 不是一层简单的 RPC 包装。它同时持有:

text
MasterClient
TransferSubmitter
TransferEngine
LocalHotCache

Client 先向 Master 请求元数据,再根据元数据选择数据面的具体操作。例如,同一个端点内可以直接复制内存,远端内存副本通过 Transfer Engine 传输,文件或卸载存储则走对应路径。

这就是 Mooncake Store 的基本分层:

text
Master:
  管理元数据、状态、租约和空间分配

Client:
  向 Master 查询事实
  调 TransferSubmitter 搬数据

Transfer Engine:
  只处理 Segment/offset/length 的字节传输

ReplicateConfig 决定副本策略

ReplicateConfig 不是装饰参数。它会影响 Put 时 Master 如何放置副本。

常见字段包括:

text
replica_num
nof_replica_num
preferred_segments
preferred_nof_segments
prefer_alloc_in_same_node
with_soft_pin
with_hard_pin

对于 SGLang/Mooncake 集成,还要额外注意版本边界:SGLang 的 Mooncake 后端会探测已安装的 Mooncake Python 包是否支持 ReplicateConfig.group_ids。如果支持,它可以把同一个 KV Cache 页对应的 K 对象、V 对象和辅助内存池对象绑定到同一分组;如果不支持,就回退到普通的 batch_put_from 对象写入路径。

在本文参考的 Mooncake 稳定标签 v0.3.11.post1 中,ReplicateConfig 还没有 group_ids 字段。因此,不能把分组语义当作该版本 Store 的基础能力;它更像是 SGLang 适配层为不同 Mooncake 包版本预留的兼容路径。

和 HiCache 的映射

SGLang HiCache 接入 Mooncake Store 时,不会把“一个请求”整体作为对象,而是把前缀 KV 按页拆开:

text
HiCache page key
  -> Mooncake 对象键
  -> Store 对象元数据
  -> 副本
  -> Segment + 偏移量 + 长度

object mapping

从 HiCache 缓存页映射为 Store 对象

Mooncake Store 不知道请求和基数树。它只看到映射后的对象键,并为每个对象维护副本状态。

HiCache 逻辑页

SGLang 推理引擎中按页管理的缓存单元

Mooncake 对象键

Store 可寻址对象

页哈希rank / TP 后缀page_kpage_v辅助池对象

Store 副本

可见性和位置

COMPLETE 副本租约Segment偏移量 / 长度

一个 MHA 缓存页通常对应 K/V 两个对象,MLA 通常只需要一个对象;混合内存池还会增加辅助状态对象。

所以 Store 看到的是一批对象,并不知道它们属于哪个请求,也不了解 SGLang 的基数树结构。这个边界必须守住。

把对象模型串起来

阅读 Mooncake Store 源码时,可以沿着一条逐层转换的链路理解:上层给出对象键和本地切片,Master 返回相应的副本信息,Client 再从副本信息中取得 Segment、偏移量和长度,最后由本地内存复制、Transfer Engine 或其他后端完成数据传输。

这条链里最容易混淆的是 Slice 和副本。Slice 描述调用方本次读写使用的本地缓冲区;副本则描述 Store 内部已经分配或提交的一份对象数据。它们会在 Put 或 Get 时配对,但不是同一个概念。

另一个关键点是状态。PROCESSING 副本说明 Master 已经预留位置,但不表示数据写入已经完成;只有 COMPLETE 副本才能被读取路径选择。只要记住这两个边界,后面的 PutStart、PutEnd、GetReplicaList 和租约就容易理解了。