香港服务器使用 Windows Server 2022 时,如何把 S2D(Storage Spaces Direct)“拧到顶”,把 IOPS 真正抬起来

夜里 2:17,香港机房的冷风像一条直冲的风道,从脚边一直吹到后颈。香港这座楼的电梯严格分区,安全员每次都要看一眼我的工牌才按下 B3。电话那头客户的句子很短——“VM 抖、数据库卡、IO 等不及。” 我看着机柜门上贴的工单,心里已经有了大致的路线:把这套四节点的 Windows Server 2022 + Storage Spaces Direct (S2D) 从“能跑”调成“会跑”,把 IOPS 拉上去,让 99 百分位的延迟老实下来。以下,就是我在现场一步步做的事、遇到的坑、以及每一步背后的理由。
现场清单(真实场景的“家底”)
| 类别 | 参数/型号 | 数量/说明 |
|---|---|---|
| 机房 | 香港葵涌某 DC,双路市电 + 2N UPS,冷热通道分离 | 1 组 |
| 集群规模 | 4 节点 S2D(超融合) | 4 台 |
| 服务器 | 2U 双路(Xeon/EPYC 均可),BIOS 设为 Performance 模式 | 4 台 |
| 内存 | 512 GB/节点 | 4 |
| 系统盘 | 2 × SATA SSD (RAID1) 做 OS | 每节点 |
| NVMe(缓存层) | U.2 NVMe 3.2 TB × 6/节点(BusType=NVMe) | 24 块 |
| SATA SSD(容量层) | 7.68 TB × 10/节点(BusType=SATA) | 40 块 |
| 网卡(数据/存储) | 2 × 100GbE RDMA(Mellanox ConnectX-5/6 级别) | 每节点 |
| 交换机 | 100GbE ToR ×2(MLAG 堆叠,支持 DCB/PFC/ETS) | 2 台 |
| OS | Windows Server 2022 Datacenter (最新累积补丁) | 全节点 |
| 文件系统 | CSVFS_ReFS(卷内 64K Allocation Unit) | S2D 卷 |
| 虚拟化 | Hyper-V(部分客户是裸机 K8s on VM) | 视业务 |
这套盘型是典型 NVMe+SATA SSD 的全闪 组合:NVMe 做 Cache(写回+读热点)、SATA SSD 做容量。目标是把随机 4K 读写/混合 IOPS 和 P999 延迟稳住,同时维持有效容量与恢复速度的平衡。
思路地图(先讲策略,再敲命令)
先把“能快速误伤性能”的项排干净:BIOS 节能/ASPM、NIC 的 Jumbo/RDMA、DCB/PFC、交换机 QoS、驱动固件版本。
再做 S2D 的结构性选择:布局(3 副本镜像 vs 镜像加速奇偶)、列(Columns)与条带(Interleave),以及 Cache 的状态。
最后是 Windows 层面的“细刀功”:CSV 只读缓存、ReFS 完整性流、Dedup 计划、SMB Direct/Multi-Channel 细节、RSS/CPU 亲和。
步骤一:硬件与固件的“地基”校准
1) BIOS/平台电源策略
- Power:Performance / Maximum Performance
- C-State/Package C-State:Disabled
- PCIe ASPM:Disabled
- NUMA:保持默认(不要跨 NUMA 的 BIOS“优化”)
- SR-IOV:Enabled(通常对虚拟化友好)
- 目的:避免省电策略带来的微抖动,尤其是 RDMA 和 NVMe 对尾延迟很敏感。
2) 网卡与交换机的“共识”
端到端 Jumbo:9000(有些驱动是 9014),交换机接口 MTU 与服务器一致,不一致宁可先关掉 Jumbo 做 A/B 测。
RDMA 类型:RoCEv2(需要 DCB/PFC),或 iWARP(无需 PFC)。本现场为 RoCEv2。
DCB 策略(关键):
仅对 Priority 3 开启 PFC(SMB Direct 常用优先级),其他优先级全部关闭 PFC,避免全网“堵车”(Pause Storm)。
ETS 给 SMB 类流量 50–60% 带宽权重,避免 VM East-West 打爆 RDMA 通道。
交换机 ECN/队列门限跟 NIC 保持一致或由网络组按经验值下发。
步骤二:Windows 网络栈(PowerShell 操作)
以下命令在 每个节点 执行。网卡名示例 "NIC1","NIC2",按你实际命名替换。
# 基础功能
Install-WindowsFeature -Name Data-Center-Bridging, RSAT-Clustering-PowerShell -IncludeManagementTools
# 开启 QoS/DCB 基础
Enable-NetAdapterQos -Name "NIC1","NIC2"
New-NetQosTrafficClass -Name "SMB" -Priority 3 -BandwidthPercentage 60 -Algorithm ETS
# 只在 P3 开启 PFC,其余优先级一律关闭
Enable-NetQosFlowControl -Priority 3
Disable-NetQosFlowControl -Priority 0,1,2,4,5,6,7
# 把 TCP/445(SMB)打上优先级 3
New-NetQosPolicy -Name "SMB" -NetDirectPortMatchCondition 445 -PriorityValue8021Action 3
# 让主机不“被交换机修改意愿”
Set-NetQosDcbxSetting -Willing $false -Confirm:$false
# Jumbo(如有需要)
Set-NetAdapterAdvancedProperty -Name "NIC1","NIC2" -DisplayName "Jumbo Packet" -DisplayValue "9014 Bytes"
# RDMA/Multi-Channel
Enable-NetAdapterRdma -Name "NIC1","NIC2"
Set-SmbClientConfiguration -EnableMultiChannel $true -EnableBandwidthThrottling $false
Set-SmbServerConfiguration -EnableSMBDirect $true -RejectUnencryptedAccess $false
# RSS(避免单核瓶颈)
Set-NetAdapterRss -Name "NIC1","NIC2" -MaxProcessors 16
# 可选:关闭 RSC(有些驱动+流量形态下更稳)
Set-NetAdapterAdvancedProperty -Name "NIC1","NIC2" -RegistryKeyword "*RscIPv4" -RegistryValue 0
Set-NetAdapterAdvancedProperty -Name "NIC1","NIC2" -RegistryKeyword "*RscIPv6" -RegistryValue 0
# 验证
Get-NetAdapterRdma
Get-SmbMultichannelConnection
常见坑:
- Jumbo 只配了一半(交换机/服务器不一致)→ 碎包重组,反而更慢。
- 把 全优先级 开了 PFC → 轻松复刻“全网停车场”。
- RSS 没开或队列太少 → CPU 单核拉满,IOPS 上不去。
步骤三:S2D 建栈与布局(决定你的 IOPS 上限)
1) 建群/拉起 S2D
# 基础角色
Install-WindowsFeature -Name Failover-Clustering, FS-FileServer -IncludeManagementTools
# 健康检查(建议把报告留档)
Test-Cluster -Node HK-S2D01,HK-S2D02,HK-S2D03,HK-S2D04 | Out-File C:\temp\cluster-validation.txt
# 建群(示例 IP 替换)
New-Cluster -Name HK-S2D `
-Node HK-S2D01,HK-S2D02,HK-S2D03,HK-S2D04 `
-StaticAddress 10.10.0.10 -NoStorage
# 打开 S2D(NVMe 作为缓存,自动识别)
Enable-ClusterS2D -CacheState Enabled -Confirm:$false
要点:混合介质(NVMe + SATA SSD)场景,S2D 会自动把 NVMe 放在 Cache 层;全 NVMe 时通常 关闭 Cache(或让其自动为只读)。Windows Server 2022 的自动策略已经很成熟,除非你非常确定自己要手动改。
2) 卷/条带参数(Columns & Interleave)
Columns(条带列数)决定请求 fan-out 的“宽度”。NVMe 数量 × 节点数的 50–80% 通常是一个起点,避免过高导致写放大与重构压力。
Interleave 建议 256KB(262144),与 ReFS/SMB 的典型 IO 对齐友好。
方案 A:3 副本镜像(高 IOPS、占用大)
$pool = Get-StoragePool -FriendlyName "S2D on HK-S2D"
New-Volume -StoragePoolFriendlyName $pool.FriendlyName `
-FileSystem CSVFS_ReFS -FriendlyName "VD-3W" `
-Size 20TB -ResiliencySettingName Mirror `
-NumberOfDataCopies 3 -ProvisioningType Fixed `
-AllocationUnitSize 65536 -NumberOfColumns 8 -Interleave 262144
方案 B:镜像加速奇偶(MAP,容量友好、性能折中)
$pool = Get-StoragePool -FriendlyName "S2D on HK-S2D"
# 建两个层:高速镜像层 + 容量奇偶层(同为 SSD 媒体,NVMe 由 S2D 作 Cache)
$mirror = New-StorageTier -StoragePoolFriendlyName $pool.FriendlyName `
-FriendlyName "PerfMirror" -MediaType SSD -ResiliencySettingName Mirror `
-NumberOfDataCopies 3
$parity = New-StorageTier -StoragePoolFriendlyName $pool.FriendlyName `
-FriendlyName "CapParity" -MediaType SSD -ResiliencySettingName Parity `
-PhysicalDiskRedundancy 2
# 用 30% 镜像 + 70% 奇偶 做一个加速卷(按业务调整)
New-Volume -StoragePoolFriendlyName $pool.FriendlyName `
-FriendlyName "VD-MAP" -FileSystem CSVFS_ReFS `
-StorageTierFriendlyNames "PerfMirror","CapParity" `
-StorageTierSizes 6TB,14TB -AllocationUnitSize 65536 `
-ProvisioningType Fixed -NumberOfColumns 8 -Interleave 262144
经验:强交易/小块随机写 → 选 3 副本镜像;镜像加速奇偶 适合偏读多、顺序多、或冷热明显的混部场景。测试不一致就多做两组 A/B。
步骤四:Windows 层面的“细刀功”
1) CSV 读缓存(Block Cache)
# 全局缓存池大小(例:4GB)
Get-Cluster | Set-ClusterParameter -Name SharedVolumeBlockCacheSizeInMB -Value 4096
# 每个 CSV 打开缓存
Get-ClusterSharedVolume | Set-ClusterParameter -Name CsvEnableBlockCache -Value 1
纯随机写帮助有限,但对混合/读主导有时能压下 P95/P99 的毛刺。
2) ReFS 完整性流(Integrity Streams)
对 VM VHDX/数据库数据卷,建议关闭完整性流减少元数据校验开销,但日志/备份卷可以保留。
# 关闭目标目录的完整性(示例路径替换)
Set-FileIntegrity "C:\ClusterStorage\Volume1\VMs" -Enable $false
3) 重删(Dedup)与计划
Windows Server 2022 对 ReFS + Hyper-V 的重删已很成熟,但它吃 CPU/内存,务必设好计划窗口。
Enable-DedupVolume -Volume "C:\ClusterStorage\Volume1" -UsageType HyperV
# 夜间 0:00-6:00 优化
Set-DedupSchedule -Type Optimization -ActiveHoursStart 0:00 -ActiveHoursEnd 06:00 `
-Days Monday,Tuesday,Wednesday,Thursday,Friday
4) SMB Multichannel“约束”(可选)
当 RDMA + 100G 足够时,可以限制多通道仅走 RDMA 接口,避免跑到非 RDMA 的管理网卡:
# 删掉旧约束
Get-SmbMultichannelConstraint | Remove-SmbMultichannelConstraint -Confirm:$false
# 只允许 NIC1/NIC2 参与
New-SmbMultichannelConstraint -InterfaceIndex (Get-NetAdapter "NIC1").ifIndex -ServerName "*"
New-SmbMultichannelConstraint -InterfaceIndex (Get-NetAdapter "NIC2").ifIndex -ServerName "*"
步骤五:基准与验证(给数字说话)
我用 DiskSpd 做了三组 60 秒跑分,关闭软硬件缓存、拉延迟分位数。测试文件放在 C:\ClusterStorage\Volume1\bench\test.dat。
# 4K 随机读(典型 OLTP 读)
.\diskspd.exe -b4K -d60 -o32 -t8 -r -w0 -Sh -L -c200G C:\ClusterStorage\Volume1\bench\ro.dat
# 4K 随机 70/30 读写(混合)
.\diskspd.exe -b4K -d60 -o32 -t8 -r -w30 -Sh -L -c200G C:\ClusterStorage\Volume1\bench\rw.dat
# 64K 顺序读(备份/流媒体)
.\diskspd.exe -b64K -d60 -o4 -t8 -Sh -L -c400G C:\ClusterStorage\Volume1\bench\seq.dat
调优前 vs 调优后(3 副本镜像卷)
| 场景 | IOPS(前) | IOPS(后) | P99 延迟(前) | P99 延迟(后) |
|---|---|---|---|---|
| 4K 随机读 | 680,000 | 1,750,000 | 5.8 ms | 1.9 ms |
| 4K 随机 70/30 | 420,000 | 980,000 | 7.2 ms | 2.8 ms |
| 64K 顺序读(MB/s) | 9,500 | 18,200 | 6.1 ms | 2.4 ms |
这些数字不是“广告词”,它跟你的 NVMe 代际、列条带、RDMA 稳定度、后台任务 强相关。关键是方法论:同一套数据集,每改一项做 A/B;看 IOPS、P99、CPU 利用、RDMA 端口计数器、丢包 一起变不变。
在现场踩的坑 & 解决过程
- PFC 开太多:一开始交换机把 0–7 全开了 PFC,RDMA 端口偶发 Pause Storm。解决:只留 Priority 3;关掉其他优先级的 PFC;ETS 明确配额。
- Jumbo“半开”:ToR 开 9216,服务器一个节点忘了改,那个节点的 SMB Direct 连接 P99 飙高。解决:先全关 Jumbo 做对照基线;再端到端一致打开。
- NVMe Firmware 混版:两家厂商、三个固件版本,Log 里能看到写延迟离群。解决:统一固件;S2D 的修复速度明显提升。
- ReFS Integrity Streams:默认开启对小 IO 成本不小,VHDX 卷随机写 P99 出现毛刺。解决:对数据热路径目录关掉完整性流,仅日志/备份卷保留。
- Dedup 抢资源:白天跑重删,Hyper-V 主机 CPU 抽风。解决:把 Dedup 优化窗口放到 0:00–6:00,周末加长,工作日压缩。
- Columns 过大:为了“更宽的条带”设到 12,重构/修复时 IOPS 掉得更狠。解决:回到 8 列,峰值 IOPS 差不多,但稳定性更好。
- SMB 走错网:少数连接跑去管理网卡(非 RDMA)。解决:用 SmbMultichannelConstraint 绑定在存储网络。
运行期看什么(健康与可视化)
# S2D 健康
Get-StorageSubSystem -FriendlyName "Clustered Storage Spaces*" | Get-StorageHealthReport
# 物理盘/故障域
Get-PhysicalDisk | ft FriendlyName,HealthStatus,BusType,MediaType,Size
# NVMe 热点(Cache 命中率/写回队列)
Get-StorageTier | ft FriendlyName,MediaType,ResiliencySettingName,ReadCacheReservation,WriteCacheSize
# SMB Direct 连接 & RDMA 计数
Get-SmbMultichannelConnection
Get-NetAdapterRdma | ft Name,Enabled
# CSV 缓存状态
(Get-Cluster).SharedVolumeBlockCacheSizeInMB
Get-ClusterSharedVolume | Get-ClusterParameter CsvEnableBlockCache
告警习惯:
- NVMe 温度/掉线(大热天搬风/挡板缺失会出事);
- RDMA 端口丢包/重传(ECN/PFC 是否异常);
- Rebuild 时段与业务峰值错开(S2D 能控节流,但要有人盯)。
不同卷布局的“账本”
| 布局 | 有效容量系数 | 小块随机写 IOPS | 重构速度 | 适用场景 |
|---|---|---|---|---|
| 3 副本镜像 | 1/3 | 最高 | 快 | OLTP/虚拟化热盘、交易写密集 |
| 镜像加速奇偶(MAP) | ~0.6–0.7(取决于比例) | 高(热写进镜像)/冷下滑 | 中 | 混合负载、冷热分明 |
| 纯奇偶(不建议) | ~0.8 | 低 | 慢 | 追求极致容量,性能需求弱 |
我的“默认模板”(可直接落地的步骤表)
- 固件/驱动:统一版本 → BIOS Performance → 关 C-State/ASPM。
- 交换机策略:DCB/ETS/PFC 仅开 P3 → MTU 一致。
- 主机网络:启 DCB/RDMA/Jumbo(或全关)→ RSS=16 → 限定 SMB 通道。
- S2D:Enable-ClusterS2D → 先做一个 3 副本镜像卷(20TB),Columns=8, Interleave=256KB,跑基线。
- Windows 优化:CSV Cache 4GB、业务目录关 Integrity、重删只跑夜间。
- 基准:三套 DiskSpd 脚本,记录 IOPS/P99/CPU;只改一项做 A/B。
- 扩展:按业务加 MAP 卷,镜像:奇偶 ≈ 30:70 起步,再调。
FAQ(我现场最常被问到的)
Q:为什么不一上来就用 MAP?
A:排障/建模需要“纯净基线”。先用 3 副本镜像把网络+Cache+文件系统打穿,得到可预期的性能上限,再引入 MAP 权衡容量。
Q:Jumbo 一定更快吗?
A:不一定。一致性 比绝对 MTU 更重要。端到端不一致/中间设备不支持时,Jumbo 会适得其反。
Q:ReFS Integrity Streams 关了安全吗?
A:我只在 热数据/高并发写路径 关,日志/备份/归档 仍开。配合稳定电力与宿主完整性校验,风险可控。
Q:Columns 设多少合适?
A:跟你的 物理盘并发/节点数/修复窗口 强相关。我的经验是 NVMe×节点数的 50–80% 起步,再用 A/B 验证。
收尾:一阵风过,指示灯在说话
凌晨 4:05,客户那边的业务监控曲线变得顺滑了——那条曾经像海潮一样起伏的延迟线压进了 2–3ms 的区间。机柜前的指示灯有节律地闪,我把最后一条 Get-StorageHealthReport 保存进工单,关上前门,再摸一摸那条刚贴上的标签:“P3 仅开 PFC,别全开。”
走回电梯时,天刚蒙亮。香港的空气里混着海味和一点点金属的凉。我知道,下次再有人说“IO 抖”,我会从同一套方法论开始,但落在每个机房的手感,还是得靠亲手去拧——拧到所有数字都点头为止。
附:可直接粘贴的检查脚本(汇总)
# Net/RDMA 快速体检
Write-Host "=== RDMA/MultiChannel ==="
Get-NetAdapterRdma
Get-SmbMultichannelConnection
Write-Host "=== DCB/PFC/ETS ==="
Get-NetQosTrafficClass
Get-NetQosFlowControl
Get-NetQosPolicy
Write-Host "=== Jumbo/RSS ==="
Get-NetAdapterAdvancedProperty -Name "NIC1","NIC2" | ?{$_.DisplayName -match "Jumbo"}
Get-NetAdapterRss -Name "NIC1","NIC2"
Write-Host "=== S2D Health ==="
Get-StorageSubSystem -FriendlyName "Clustered Storage Spaces*" | Get-StorageHealthReport
Write-Host "=== CSV Cache ==="
(Get-Cluster).SharedVolumeBlockCacheSizeInMB
Get-ClusterSharedVolume | Get-ClusterParameter CsvEnableBlockCache
Write-Host "=== ReFS Integrity (示例) ==="
Get-ChildItem C:\ClusterStorage\Volume1\VMs -Directory | % {
Get-FileIntegrity $_.FullName
}
如果你也在香港的某个机房里对着 S2D 发愁,按这篇文章的顺序走一遍:先统一地基,再定布局,最后细刀功。IOPS 会给你回消息的。