Storage Spaces Direct(S2D)的数据块映射

S2D(Storage Spaces Direct)的 chunk mapping(数据块映射),本质是它整个分布式存储系统的"核心地址系统"。你可以把它理解为:

把一个虚拟磁盘(VHDX/CSV 卷)中的逻辑数据块,映射到集群中所有物理磁盘上的"分布式坐标系统"。

一、先给结论:S2D 的 chunk mapping 是什么?

S2D 使用的是一种:虚拟化分层映射结构(Virtualized Extent-Based Mapping)

核心结构:

复制代码
Volume (CSVFS)
  ↓
Storage Spaces Virtual Disk
  ↓
Slab / Chunk Mapping Table(核心)
  ↓
Extents(逻辑块)
  ↓
Physical Disk + Fault Domain

详细描述:

  • CSVFS(Cluster Shared Volume File System) 是 S2D 最常用的接入层,但它本身不负责"块在哪"。CSV 只解决"多个节点同时挂载同一个卷、并发访问不冲突",把 I/O 重定向到协调节点(coordinator node)。也就是说,S2D 的"地址"在 CSV 之下,不在 CSV 之上。
  • 这个分层的好处是:上层的 NTFS、应用程序、Hyper-V 看到的还是普通块设备(VHDX/CSV),完全感知不到底下是几十块 NVMe 还是混插的 HDD+SSD。
  • Virtual Disk 这一层是 S2D 自己抽象出来的"逻辑盘",对应 Get-VirtualDisk 的对象。Slab/Chunk Mapping Table 是 S2D 内部维护的"私货",对外不可见。

二、S2D 的"Chunk"到底是什么?

在 S2D 中,"chunk"通常指:固定大小的逻辑分配单元(Extent / Slab)。

典型粒度:

  • 1 GB(常见 slab size)
  • 或更细粒度 extent

⚠️ 注意:它不是 RAID stripe unit,而是虚拟存储分配单位。

详细描述:

  • Slab 是 S2D 里的"分配单位"(allocation unit)。一个 Virtual Disk 会被切成很多个 Slab,每个 Slab 独立决定副本策略、独立做故障恢复。所以 Slab 不能太小(否则映射表爆炸),也不能太大(否则小文件浪费空间)。1 GB 是 S2D 的默认值,也是工业界共识。
  • Extent 在 S2D 内部文档里更接近"chunk",是 S2D 内部用于 I/O 调度的更小单位。一个 Slab 由若干个 Extent 组成,Extent 的粒度通常和存储总线/扇区对齐,常见 256 KB 或 1 MB。
  • Slab ≠ Stripe Unit。RAID-5/6 的 stripe unit 决定的是"一次写跨几个盘、校验块放哪";S2D 的 Slab 决定的是"一段连续逻辑地址放哪几个盘",不参与条带打散(S2D 的 parity 是按 Slab 分组的,而不是按字节条带)。
  • 还有一个容易混淆的概念是"interleave",是 S2D Virtual Disk 的一个属性,描述"一次 I/O 跨多少块物理盘"做并行写入。Interleave 和 Slab 是两个正交的东西,一个管"扇区怎么散",一个管"块放哪里"。

三、核心结构:三层映射模型

S2D chunk mapping 不是一层,而是三层结构:

1️⃣ 第一层:虚拟卷 → Extent(逻辑层)

例如:

复制代码
VHDX / CSV Volume
  ↓
Logical Block Address (LBA)

被切分为:

  • 1 MB / 256 KB(I/O 粒度)
  • 汇聚成 extents(逻辑连续块)

2️⃣ 第二层:Extent → Slab(S2D 核心)

这是关键层:Slab = S2D 的分配与调度单位

每个 slab:

  • 大小通常为 1 GB
  • 属于某个虚拟磁盘(VDisk)
  • 绑定副本策略(mirror / parity)

映射表:Slab ID → {Disk1, Disk7, Disk12}

  • 2-way mirror → 2 个物理位置
  • 3-way mirror → 3 个物理位置
  • Parity(单奇偶)→ 1 数据 + 1 校验(多盘)
  • 双奇偶 → 1 数据 + 2 校验

3️⃣ 第三层:Slab → Physical Disk Extent

最终落到:

  • NVMe / SSD / HDD

  • 具体 offset(物理地址)

    Slab

    Physical Extent

    Disk sector address

详细描述:

  • LBA 到 Extent 的切分取决于两个东西:底层磁盘的"physical sector size"(S2D 默认按 4 KB 对齐,但如果是高级格式 512e 也会兼容),以及 Get-VirtualDisk | Get-StorageTierInterleave 属性。
  • Mirror 和 Parity 是 Per-Slab 策略。这意味着同一个 VDisk 里,可以有"前面 100 GB 是三副本,后面 1 TB 是双副本"------实际生产中很少这么用,但能力是具备的。
  • 副本 / 校验的具体盘不是 Slab 内部决定的,而是由 S2D 的 placement 算法 决定的。Slab 表格里只记录"放哪了",不记录"为什么放那"。
  • Sector address 落到磁盘时,S2D 还会再走一层"physical disk 的 sector 偏移"。如果你的盘是分区过的(partitioned),S2D 会自动避开分区表所在区域,避免把元数据写到分区头。

四、S2D Chunk Mapping 的核心数据结构

内部维护一个关键结构:Mapping Table(类似分布式 FAT / inode)

字段 含义
Slab ID 逻辑块编号
Policy mirror / parity
Fault Domain 节点 / 机架
Disk list 实际存储位置
Offset 物理位置
Health state 是否降级

详解描述:

  • 这张表在 S2D 里叫 "allocation map" 或 "slab allocation map",由 Cluster Service(ClusSvc) 统一管理,不会 单独存在某一个节点上。它通过集群注册表 / 分布式协调(类似 Raft 思路)保持一致。所以即使一个节点掉电,这张表也不会丢。
  • Health state 至少包含:Healthy / Degraded(一个副本丢了)/ Shrinking(正在回收)/ InRepair(正在重建)。Get-VirtualDisk | Get-StorageReliabilityCounter 能看到一部分。
  • Disk list 不是写死的------它会随时间漂移。系统会周期性调用"rebalancer",根据当前 IO 热度、容量、故障域分布,悄悄 调整某些 Slab 的位置(业内叫 slab rebalancing )。这意味着你在两台机器上跑 Get-PhysicalDisk | Get-StorageReliabilityCounter 看到的"在某盘上分配了多少"会变化。
  • 没写进表的内容:S2D 的 mapping table 里不存数据本身(这是显然的),也不存文件路径 / 文件名。文件级元数据归 NTFS / ReFS 管;chunk mapping 只管"盘上的字节属于哪一块逻辑地址"。

五、一个真实写入示例

假设:

  • 2-way mirror
  • 3 节点集群(Node A / B / C)
  • 写入一个 4 MB 文件

Step 1:切块 4 MB → 4 × 1 MB extents

Step 2:分配 slab

Extent Slab
E1 S1001
E2 S1002
E3 S1003
E4 S1004

Step 3:映射到物理磁盘(chunk mapping)

复制代码
S1001:Primary: Node A / NVMe0     Replica: Node B / NVMe1
S1002:Primary: Node B              Replica: Node C
S1003:Primary: Node C              Replica: Node A

Step 4:写入路径

复制代码
App write
  ↓
CSVFS
  ↓
SBL (Storage Bus Layer)
  ↓
Chunk mapping lookup
  ↓
Parallel write to disks

详细描述:

  • SBL(Storage Bus Layer) 是 S2D 内核态的核心组件,它把"逻辑 I/O"翻译成"对 Physical Disk 的具体 I/O 请求"。如果把整个 S2D 比作一个数据库,SBL 就是"执行引擎"。
  • CSVFS 的协调节点(coordinator) 不一定和 Primary 节点一致。比如应用跑在 Node A,Node A 是 coordinator,I/O 被 forward 到 Node B(B 才是 Slab S1001 的 Primary 之一)。S2D 不要求"写主本必须走 Primary 节点",这是它和某些 SAN 协议不一样的地方。
  • 写盘顺序:mirror 模式下 S2D 默认不是同步双写,而是 "write-back cache + 异步同步"。如果开了 Storage Bus Cache(混插 S2D 里的"性能层"),数据会先写 SSD cache,再 async 落 HDD。这对延迟很敏感的应用(SQL Server 事务日志、Hyper-V VHDX runtime)是关键。
  • 写穿(write-through)模式:如果你用纯 NVMe 全闪 S2D(没有混合层),行为退化为"两份都落盘才返回成功",延迟更稳但 RT 更高。
  • Parity 写入比 Mirror 复杂得多。Parity 模式下 S2D 还要做"读改写"(read-modify-write)和"重组"(reconstruct write)。一次 4 KB 写入可能涉及读出整个 stripe(几 MB)、计算新校验、写回。这是 S2D Parity 比 Mirror 慢几十倍的根本原因。

六、S2D Chunk Mapping 的关键特性

1️⃣ 动态分布(Dynamic Placement)

不固定 RAID stripe,每个 slab 可在任何磁盘。 类似 Ceph CRUSH,但微软实现。

2️⃣ 故障域感知(Fault Domain Awareness)

mapping 会避免:同一节点、同一机架放同一副本。

3️⃣ 自动重建(Rebalance)

当:新加节点 / 磁盘失效 时,系统会 Re-map slabs → 重新分布。

4️⃣ 负载均衡(IO aware placement)

不是只看容量,还看:IO 热度、latency、queue depth。

详细描述:

  • 故障域(Fault Domain, FD) 不仅是节点和机架。在 Azure Stack HCI 里,FD 还能细到"机箱(chassis)"、"机柜顶(rack top)"、"电源回路"。Get-ClusterFaultDomain 可以看完整层级。一个 3-way mirror 的副本一定落在 3 个互不重叠的故障域里。
  • Rebalance 不是瞬间完成。新加节点后,Start-StorageReliabilityCounter 或 storage job 会在后台慢慢搬 slab,速率被限流(避免影响业务 IO),整个池可能要几小时到几天才完全 rebalance 完。期间性能会有抖动。
  • 磁盘失效 vs 节点失效 的处理路径不一样:
    • 单盘坏:S2D 触发 "Repair" job,按 Slab 重建坏的那个副本。
    • 整机掉:cluster 先仲裁(quorum)------超过半数节点在线才继续服务;只要还能保持 quorum,受影响 Slab 进入"degraded + awaiting repair",修复由剩余节点上的副本承担。
    • 仲裁丢失:所有 Slab 暂时停服。这是为什么 S2D 集群推荐 2N+1 节点(容忍 1 节点挂)而不是 2N。
  • IO 感知的再分布 在 S2D 里叫 "Storage Aware Placement",但它不是实时的。是后台周期任务,间隔几十分钟到几小时。所以你会看到"刚加完节点,第一天没动静,第二天才看到磁盘读写开始迁移"这种现象。

七、Chunk Mapping vs RAID

特性 S2D Chunk Mapping RAID
单位 slab (1 GB) stripe unit
管理方式 分布式映射表 控制器固定
扩展性 线性扩展 有上限
重建 slab 级别 disk 级别
故障域 节点级 磁盘级

详细描述:

  • 扩展性 这一点是 S2D 的最大卖点。传统硬件 RAID 控制器有最大盘数限制(一般 16-32),且性能随盘数非线性增长(控制器是瓶颈)。S2D 因为 placement 在软件层做,且分布到所有节点,能扩展到 4 节点 / 16 节点 / 64 节点(Azure Stack HCI 单集群上限)。
  • RAID 的"重建"是替换整盘------换了新盘,控制器把整盘数据按 stripe 顺序重新算出来写回去。S2D 的"重建"是按 Slab 重建------只重建那些"副本数不足"的 Slab,其他 Slab 根本不需要动。所以 S2D 重建时 IO 影响远比 RAID 小(且是后台限流的)。
  • RAID 没有"故障域"概念,它的"冗余度"是盘级别的。S2D 故障域是节点/机架级------这意味着即使坏一块盘,cluster 还会照常服务;即使坏一个节点,剩下的节点会接手所有该节点上的 slab 重映射。这就是 S2D 能"在线换节点"的原因。
  • RAID 5 的写入惩罚(write penalty) 在 S2D Parity 上也存在,但 S2D 通过全闪性能层 + 写回 cache 缓解。
  • 一句话总结:S2D 把"RAID 卡"做的事情,抽到了分布式软件层,且粒度从"盘"降到了"slab"。

八、一个关键理解

S2D chunk mapping 本质不是"存储结构",而是:一个分布式一致性"数据地址系统"(Distributed Storage Addressing Layer)

它同时负责:

  • 数据放哪里
  • 副本在哪
  • 如何恢复
  • 如何迁移
  • 如何负载均衡

详细描述:

  • "分布式一致性" 是关键词------S2D 的 mapping table 是集群范围内一致的,这和"每台机器各管各的 RAID"有本质区别。任何一次 slab 迁移、任何一次副本重分配,都需要集群所有节点达成一致。
  • "地址系统" 而不是 "存储系统"------注意区分。S2D 的 mapping 只管"地址 → 物理位置",不管"这些位置上放的是什么字节"。"字节"本身由底层 Physical Disk 负责,文件结构由 NTFS / ReFS 负责。
  • 它同时是五件事:placement(放置)、replication(复制)、repair(恢复)、migration(迁移)、balancing(均衡)。传统存储系统通常只能做其中一两件;S2D 把它打包成一张"统一表",是它能像超融合一样灵活的根本原因。
  • 类比真实分布式系统:S2D 的 chunk mapping 接近 Ceph 的 OSD map + PG map 的合体,但比 Ceph 简单(Ceph 的 CRUSH 算法有显式权重 / 拓扑规则,S2D 是内部启发式);也类似 HDFS 的 NameNode 元数据,但 NameNode 是单点,S2D 是分布式共识。

九、类比

系统 类比
RAID 单机数组
NTFS 文件系统 inode
S2D chunk mapping 分布式 inode + 路由表
Ceph CRUSH 同类系统(但更显式算法)

详细描述:

  • 和 Ceph 的更精确对比:
    • Ceph PG ≈ S2D Slab(都是分配单位)
    • Ceph OSD ≈ S2D Physical Disk
    • Ceph OSD Map / CRUSH Map ≈ S2D Fault Domain 配置
    • Ceph Pool ≈ S2D Storage Pool / Virtual Disk
    • 核心区别:Ceph 显式分离"OSD Map(盘在不在)+ CRUSH Map(怎么放)+ PG Map(数据归谁)"三张表;S2D 把它合并成一张 distributed mapping table。这是 S2D 部署更简单、但精细控制不如 Ceph 的原因。
  • 和传统 SAN 的对比:传统 SAN(NetApp / VMware vSAN / 华为 OceanStor)的"地址系统"通常叫 LUN Mapping / VMFS Metadata------它们基本是"控制器单点决策 + 客户端缓存"模型,扩到几十节点就吃力。S2D 是端到端分布式决策,这是它能跑超融合的根本。
  • 建立直觉的最快方式:把 S2D 想象成"每块盘都自己是一台小 Ceph OSD",而 chunk mapping 就是这些 OSD 共同维护的"分布式大脑"。