香港服务器使用 Windows Server 2022 时,如何调优存储 QoS 策略,保障关键应用的磁盘 I/O 优先级?

清晨 6:40,我在铜锣湾机房的冷通道里被一阵报警声迎面拍醒。夜班同事把 KVM 推给我:“SQL 的延迟又飙到了 80ms,电商 API 跟着抖,似乎是报表 VM 在扫全库。”我戴上耳机、蹲在第 3 排第 7 柜前,风扇像飞机起飞,手心里全是冷风吹出的干燥感。这不是第一次被“吵闹的邻居”拖慢关键业务,但这次我决定从根上把它管住——用 Windows Server 2022 的存储 QoS(Storage QoS) 给关键应用 硬性安排 I/O 优先级。
下面是一整套我落地的做法与踩过的坑,既给新手提供一步一步的“能复刻”流程,也给老手提供在香港高密机房环境里可操作的“深水区”经验。
一、现场环境与硬件清单(真实可复刻)
目标:保障 SQL01(关键业务数据库 VM)的磁盘 I/O 优先级,抑制 BI-ETL、FullScan 等报表 / 批处理 VM 在高峰期“抢占” I/O。
机房/基础设施
- 位置:香港、铜锣湾,单柜 42U,2×10A 供电,冷/热通道隔离
- 运营商:本地三线 BGP,跨境链路单独 QoS(网络层)
主机与存储(Hyper-V + S2D)
集群:4 节点 Windows Server 2022 Datacenter(Core) + Hyper-V + Storage Spaces Direct(S2D)
服务器:Dell PowerEdge R740xd(2U)
- CPU:2× Intel Xeon Gold 6248R(3.0GHz,24C/48T)
- 内存:384GB
- NIC:Mellanox ConnectX-5 25GbE ×2(RoCEv2,SMB Direct)
磁盘(每节点)
- NVMe(Cache):4× 1.6TB U.2 NVMe
- SSD(容量层):8× 3.84TB SAS SSD
- 文件系统:ReFS,CSV
- S2D 布局:Mirror-accelerated Parity(热写入走镜像,冷数据落奇偶)
关键 VM(示例)
- SQL01:16 vCPU / 128GB RAM,VHDX:Data 1TB(4K 对齐)、Log 256GB(64K 格)
- API01:8 vCPU / 32GB RAM,系统+应用盘
- BI-ETL:12 vCPU / 64GB RAM,报表与全库扫描
- BG-Tasks:备份、全文索引、批处理等
SLA(我们要兜住的底线)
- SQL01:P99 读延迟 ≤ 5ms,写延迟 ≤ 10ms
- API01:P99 总体 ≤ 8ms
- 其他 VM:不得压制关键 VM 的 IOPS,但允许峰值受限
说明:Windows 的 存储 QoS 是基于 8KB 归一化 I/O 计算 IOPS 的。也就是说,你设定的 MinimumIops/MaximumIops 都是以 8KB 为基准的“等效 IOPS”。
二、方案选型:主机级 QoS vs 集中式(S2D/SOFS)QoS
Windows Server 2022 提供两种思路:
主机级(Host-initiated)QoS:
在 Hyper-V 主机上直接对 VM 的 VHDX 设定最小/最大 IOPS。
- 适合:单机/小规模;VHDX 不在 SOFS 共享上。
- 命令:Set-VMHardDiskDrive -MinimumIOPS/-MaximumIOPS
集中式(Cluster/SOFS)QoS(推荐):
- 在 S2D / SOFS 集群中创建 QoS 策略(Dedicated/Aggregate),在主机上将策略绑定到 VM 的 VHDX;
- 支持全局视角的“最小保底 + 最大封顶”,对CSV/SMB 上的 VHDX 精准生效;
- 命令:New-StorageQosPolicy、Set-VMHardDiskDrive -QoSPolicyID ...、Get-StorageQosFlow。
- 我这套环境是 S2D,选用了集中式 QoS。但下面两个方案我都会给到完整步骤,方便你按场景选。
三、上手前的“基线”:量化你现在到底差在哪儿
1)性能计数器(PerfMon)建议
- \LogicalDisk(*)\Avg. Disk sec/Read、\Avg. Disk sec/Write、\Disk Transfers/sec
- \Hyper-V Virtual Storage Device(*)\Average Read Latency、\Average Write Latency
- \SMB Client Shares(*)\Avg. Read/Write Latency
- \Cluster CSVFS(*)\Redirected I/O(排查 CSV 是否重定向)
- \Process(SQLSERVR)\IO Data Bytes/sec(业务视角)
2)一次可复现的压测(DiskSpd)
在干扰源和被保障两类 VM 各运行一轮,记录前后变化。(以下示例在非业务高峰期执行)
# 8KB 随机读写 30%,并发 8 线程,每线程队列深度 32,持续 60s
# 不使用系统缓存(-Sh),输出延迟分布(-L)
.\diskspd.exe -b8K -d60 -r -w30 -t8 -o32 -Sh -L D:\test.dat 10G
我上现场时的基线(节选)
| VM | 指标 | 峰前(无 QoS) | 峰时(无 QoS) |
|---|---|---|---|
| SQL01 | P99 读延迟 | 4.2ms | 78.3ms |
| SQL01 | P99 写延迟 | 6.1ms | 92.7ms |
| BI-ETL | 吞吐(等效 IOPS) | 15k | 45k |
| API01 | 平均响应时间(上层 APM) | 25ms | > 300ms |
结论:典型 噪声邻居(Noisy Neighbor);峰时 BI-ETL 抢占 I/O,SQL01 延迟爆炸。
四、确定 QoS 目标值(不是“拍脑袋”)
我按业务侧给的 SLA,结合底层 NVMe/SSD 的能力与峰值同时在线的 VM 数,给出以下归一化(8KB)目标:
- SQL01-Data:MinIOPS = 10,000,MaxIOPS = 40,000
- SQL01-Log:MinIOPS = 3,000,MaxIOPS = 10,000(日志以顺写为主,但要稳)
- API01:MinIOPS = 2,000,MaxIOPS = 8,000
- BI-ETL / BG-Tasks(聚合上限):总 MaxIOPS = 20,000(避免抢占)
小技巧:Min 总和请 ≤ 物理可用 IOPS 的 60–70%,留出波动与瞬时突发的喘息空间。Max 的总和可以超过物理能力,但关键是 Min 的兜底。
五、实施路径 A:主机级(Host)QoS(适合小规模/非 SOFS)
若你的 VHDX 在本地存储或非 SOFS 共享,且没有 S2D 集群,可用这一条。
1)对关键盘设定最小/最大 IOPS
# Data 盘(举例:SCSI 控制器 0 的 Location 1)
Set-VMHardDiskDrive -VMName "SQL01" -ControllerType SCSI -ControllerNumber 0 -ControllerLocation 1 `
-MinimumIOPS 10000 -MaximumIOPS 40000
# Log 盘(Location 2)
Set-VMHardDiskDrive -VMName "SQL01" -ControllerType SCSI -ControllerNumber 0 -ControllerLocation 2 `
-MinimumIOPS 3000 -MaximumIOPS 10000
# API01
Get-VMHardDiskDrive -VMName "API01" | Set-VMHardDiskDrive -MinimumIOPS 2000 -MaximumIOPS 8000
2)为“吵闹的邻居”封顶
Get-VMHardDiskDrive -VMName "BI-ETL" | Set-VMHardDiskDrive -MaximumIOPS 20000
Get-VMHardDiskDrive -VMName "BG-Tasks*" | Set-VMHardDiskDrive -MaximumIOPS 15000
3)校验
Get-VMHardDiskDrive -VMName "SQL01" | ft Path,MinimumIOPS,MaximumIOPS
这一方案简单直接,但无法聚合多个 VM 的总上限(每盘单独封顶),跨主机/跨卷全局视角弱。
六、实施路径 B:集中式(S2D/SOFS)存储 QoS(推荐)
适用于 VHDX 存在 CSV/SMB 共享(S2D 或 SOFS),能够 统一创建策略 并 在主机上绑定 到具体盘。
1)在存储集群(SOFS/S2D 任一节点)创建策略
# 进入存储集群节点(或通过 PS Remoting)
# 专用策略(Dedicated):一盘一个“配额”与“保底”
New-StorageQosPolicy -Name "SQL-Data" -PolicyType Dedicated -MinimumIops 10000 -MaximumIops 40000
New-StorageQosPolicy -Name "SQL-Log" -PolicyType Dedicated -MinimumIops 3000 -MaximumIops 10000
New-StorageQosPolicy -Name "API-Vol" -PolicyType Dedicated -MinimumIops 2000 -MaximumIops 8000
# 聚合策略(Aggregate):多个 VHDX/VM 共享“总上限”
New-StorageQosPolicy -Name "Batch-Group" -PolicyType Aggregate -MaximumIops 20000
Get-StorageQosPolicy | ft Name,PolicyId,PolicyType,MinimumIops,MaximumIops
2)在 Hyper-V 主机上绑定策略到具体 VHDX
# 拿到策略 ID
$polData = Get-StorageQosPolicy -Name "SQL-Data"
$polLog = Get-StorageQosPolicy -Name "SQL-Log"
$polAPI = Get-StorageQosPolicy -Name "API-Vol"
$polBatch= Get-StorageQosPolicy -Name "Batch-Group"
# 绑定 SQL01 的 Data、Log 盘(用精确的控制器位置)
Get-VMHardDiskDrive -VMName "SQL01" -ControllerType SCSI -ControllerNumber 0 -ControllerLocation 1 `
| Set-VMHardDiskDrive -QoSPolicyID $polData.PolicyId
Get-VMHardDiskDrive -VMName "SQL01" -ControllerType SCSI -ControllerNumber 0 -ControllerLocation 2 `
| Set-VMHardDiskDrive -QoSPolicyID $polLog.PolicyId
# API01 绑定
Get-VMHardDiskDrive -VMName "API01" | Set-VMHardDiskDrive -QoSPolicyID $polAPI.PolicyId
# 把 BI-ETL 与 BG-Tasks 丢进“聚合上限”
Get-VMHardDiskDrive -VMName "BI-ETL" | Set-VMHardDiskDrive -QoSPolicyID $polBatch.PolicyId
Get-VMHardDiskDrive -VMName "BG-Tasks*" | Set-VMHardDiskDrive -QoSPolicyID $polBatch.PolicyId
3)实时监控流(Flow)是否达标
Get-StorageQosFlow |
select InitiatorName,FilePath,PolicyId,Status,MinimumIops,MaximumIops,CurrentIops,AverageLatency |
ft -AutoSize
Status 若显示 InsufficientThroughput,说明物理层无法满足 Min,要么调小 Min,要么加盘/加节点。
七、验证:压测 + 业务观测的双闭环
压测回放(节选)
| 场景 | SQL01 P99 读延迟 | SQL01 P99 写延迟 | BI-ETL IOPS(等效) |
|---|---|---|---|
| 无 QoS(峰时) | 78.3ms | 92.7ms | 45k |
| 主机级 QoS(只封顶) | 12.1ms | 15.7ms | 20k |
| 集中式 QoS(Min+Max 生效) | 4.6ms | 7.2ms | ≤20k(聚合) |
业务监控(APM)
- API01 的请求 P95 从 220ms 回落到 40–60ms;
- CFO 最关心的结算模块延迟,峰时稳定在 80ms 以内(应用层),不再随报表扫库抖动。
八、生产可用的“看板级”脚本(简洁可抄)
下面脚本每 5 秒输出关键 VM 的 QoS 达标情况;必要时你可以把它改成事件日志+邮件告警。
$critical = @("SQL01","API01")
while ($true) {
$flows = Get-StorageQosFlow |
where {$_.InitiatorName -in $critical} |
select @{n='VM';e={$_.InitiatorName}},
@{n='IOPS';e={$_.CurrentIops}},
@{n='Min';e={$_.MinimumIops}},
@{n='Max';e={$_.MaximumIops}},
@{n='AvgLat(ms)';e={[math]::Round($_.AverageLatency,2)}},
Status
cls
$flows | ft -AutoSize
Start-Sleep -Seconds 5
}
九、那些“看上去不像坑、但很要命”的坑(现场处置纪要)
策略不生效
原因:VHDX 不在 CSV/SMB(SOFS) 上;或用了直通磁盘/物理盘。
处理:把 VHDX 放到 CSV/SMB 共享。直通盘不受 QoS 控制,尽量避免。
RDMA 没开,SMB 直连打回传统 TCP
症状:CPU 飙高,延迟飘;Get-SmbMultichannelConnection 看不到 RDMA。
处理:确认 DCB(PFC/ETS)一致、交换机 RoCEv2 配置正确、NIC 固件/驱动匹配。
电源计划是“平衡”而非“高性能”
症状:偶发延迟“锯齿”,CPU C-State 过深。
处理:设 高性能;BIOS 里关深 C-State,开 Performance Profile。
ReFS + 即时完整性扫描
症状:后台校验与业务抢 I/O。
处理:把重扫描窗口挪到业务低谷;备份/校验避开高峰。
NVMe/SAS 固件与 HBA 模式
症状:队列深度被“锁小”,Min 达不到。
处理:检查 HBA IT(直通)模式,统一固件;队列深度至少 128 起。
单位搞错:IOPS vs MB/s
提示:QoS 的 Min/Max 是 8KB 等效 IOPS,不要填成 MB/s 上限。
动态 VHDX 在高水位扩容瞬间延迟抖
处理:关键盘尽量用 固定大小(Fixed)VHDX,或预扩到足够空间。
过度承诺 Min(Min 总和 > 物理能力)
症状:大量 InsufficientThroughput;全场都不快乐。
处理:重新盘点物理 IOPS,Min 控制在 60–70%,逐步上调。
十、经验性参数与“口袋表”
QoS 策略口袋表(本例)
| 策略名 | 类型 | MinIOPS | MaxIOPS | 适用对象 |
|---|---|---|---|---|
| SQL-Data | Dedicated | 10000 | 40000 | SQL01 数据盘 |
| SQL-Log | Dedicated | 3000 | 10000 | SQL01 日志盘 |
| API-Vol | Dedicated | 2000 | 8000 | API01 系统/应用盘 |
| Batch-Group | Aggregate | — | 20000 | BI-ETL、BG-Tasks |
建议的监控阈值
- Get-StorageQosFlow.Status != OK —— 触发告警
- AverageLatency > 15ms(关键 VM)—— 黄色预警
- Min 满足率 < 95%(自定义指标)—— 黄色预警
十一、FAQ:几个关键实现细节
Min 什么时候“起作用”?
当系统检测到存在 I/O 竞争时会优先满足 Min,调度器会在几秒内收敛,不是瞬时值。
Max 是硬上限吗?
是的。达到 Max 后,额外 I/O 会被节流,不再向下压关键 VM 的 Min。
Aggregate(聚合)怎么理解?
多个盘/VM 共享一个“总帽子”。比如把所有报表/备份放到 Batch-Group,它们“互相抢”,但永远不越过 20k IOPS 的总阀。
和应用层限速(比如 SQL Resource Governor)怎么配合?
建议 存储层守“硬线”,应用层做“精细配额”。存储层先确保关键 VM 不被拖垮。
十二、收尾:把“吵闹的邻居”变成“安静的同住者”
早上 9:30,我们在冷通道旁的折叠桌上看监控曲线慢慢变平。SQL01 的延迟像被按住的一根琴弦,再也不抖。BI 的报表照跑,但再也不可能把关键业务拖下水。我拍了拍 KVM 车,跟同事说:“以后报表想闹也闹不起来了,该干嘛干嘛。”
这就是 Windows Server 2022 存储 QoS 真正的价值:
不靠“希望”,靠 策略;不靠“人盯人”,靠 系统级调度。
附录 · 一键复查清单(可直接执行/核对)
# 1) 确认 RDMA 与 SMB 直连
Get-SmbMultichannelConnection | ft ClientIP,ServerIp,ServerName,ClientInterfaceIndex,RDMA
# 2) 列出所有 QoS 策略
Get-StorageQosPolicy | ft Name,PolicyId,PolicyType,MinimumIops,MaximumIops
# 3) 查看关键 VM 的盘与策略绑定
Get-VMHardDiskDrive -VMName "SQL01" | ft Path,ControllerType,ControllerLocation,MinimumIops,MaximumIops,QosPolicyID
# 4) 实时流量与状态
Get-StorageQosFlow |
select InitiatorName,FilePath,PolicyId,Status,MinimumIops,MaximumIops,CurrentIops,AverageLatency |
sort InitiatorName | ft -AutoSize
# 5) 电源与 CPU 状态(确保高性能)
powercfg /GETACTIVESCHEME
如果你也在香港,面对高密机房、有限供电、跨境突发流量、以及“吵闹的邻居”,不妨照着这套做一遍。先定好 Min 的底线,再给不重要的 VM 戴好 Max 的“安全帽”。当你在冷通道里把最后一条脚本敲下去、曲线重新收敛的那一刻,你会知道:这不是“玄学调优”,这是真正可验证、可复制的工程化治理。