Appearance
Windows 存储池踩坑和调优
本人的存储资源经过几次变迁,目前主要由两个 8T 的 CD6 和 6 个 18T 的 HC550 组成。需求如下
- 支持 SSD 做缓存、HDD 做阵列
- 支持 RDMA
由于内网需要 Windows 主机部署一些只有 win 下才能部署的服务(比如 wac 等管理服务)。TrueNAS 之类的系统又不支持 RDMA,因此最后还是选择了 WinServer 做 NAS
奇偶校验调优
WIN 下的奇偶校验磁盘性能一直是一个问题,虽然这个问题我一般不怎么在意,但是进行 PT 下载的时候经常写爆,写入速度只有可怜的 70MB/S。这个问题实在是绷不住了,只不过一直以来也没什么好办法
扩展阅读
奇偶校验是有写入惩罚机制的,比起直接写的镜像数据,RAID5 每次写入数据需要读取原有数据、读取校验位、写入新数据、写入校验数据这四个步骤。因此写入的惩罚系数是 4,RAID6 因为多一个校验位,需要读和写两次校验位,因此写入惩罚系数是 6。也就是说一个 6 盘的 RAID6 阵列的写入速度和单盘是一样的
条带写技术就是这个问题的解决方案。简言之就是一次攒满一个条带的数据再写入。这样就不需要读写校验位了,直接计算后写入即可
直到有一天我知道了 win 也能进行条带写,实在是绷不住了。白忍了这么久的低速阵列
具体的原理是 win 新建卷的时候会制定一个分配单元大小,在创建虚拟磁盘的时候手动指定每个磁盘的交错(神奇翻译,指每个磁盘的逻辑分片)的大小,让数据磁盘数
x交错大小
=分配单元大小
,这样就能正好一次写入就是一个条带
这个匹配过程说简单也简单,说复杂也复杂。NTFS 支持512/1024/2048/4096/8192/16K/32K/64K/128K/256K/512K/1M/2M
的簇大小,ReFS 只支持4096/64K
的簇大小。逻辑簇大小似乎最小就是 16KB,也就是说 5 盘 Raid5 和 6 盘 Raid6 是最合适的
- 分配单元大小:64K,NTFS 最高为 2M(ReFS 不行,逆天)。这个越小越好,一个最小的文件也会占用一个单元
- 交错大小:默认为 256 KB。微软设定这个值需要在 16K-16M 之间,越小越好
- 硬盘数量:
(总硬盘数-冗余硬盘数)x交错大小=分配单元大小
。因为单元大小是定值、交错大小最小 16K。所以只能是2的N次方+冗余盘数
配置速查表,分配单元默认 16K。条带写入对阵列磁盘数的要求其实非常高。如果想要镜像加速奇偶校验(需要 ReFS),你只能有 4 块数据硬盘,非常折磨。唯一值得庆幸的是 6 盘本来就是常见的 R6 阵列数量,消费级一般也就给你 6 个 SATA 通道
数据硬盘数量 | R5 硬盘数 | R6 硬盘数 | 分配单元 | 文件系统 |
---|---|---|---|---|
4 | 5 | 6 | 64K | ReFS/NTFS |
8 | 9 | 10 | 128K | NTFS |
16 | 17 | 18 | 256K | NTFS |
参考资料
以我现在的阵列为例,6 个机械组双奇偶校验阵列。格式化成 ReFS,数据磁盘数是 6-2=4,Refs 的卷大小是 4K 或者 64K,这里选择 64K。因此逻辑簇的大小就是 64K/4=16K
正好手上有一堆测试用的小垃圾磁盘。目前是两个 4T 的 9A3,和 6 个 15K 转速的 600G 机械。简单的测试一下
从测试结果看,确实是从未优化的 50M 提升到了平均 170M。效果立竿见影,3 > 提升倍数 > 4
的结果符合预期,但是奇怪的是为什么只有 170M,这些 SAS 盘似乎有问题。单独的磁盘测速希捷的只有 120M,东芝的则是正常的 230M。之前朋友的 6 个坏了一半的 500G 普通 3.5 机械的速度也有 450MB
powershell
# 命令分成两块,因为似乎New-Volume似乎没法设定磁盘大小
New-VirtualDisk -StoragePoolFriendlyName "Test" -FriendlyName "ArchivalData" -UseMaximumSize -ProvisioningType Fixed -ResiliencySettingName "Parity" -PhysicalDiskRedundancy 2 -NumberOfColumns 6 -Interleave 256KB
Get-VirtualDisk | Where-Object FriendlyName -eq 'ArchivalData'|
Initialize-Disk -PartitionStyle GPT -PassThru |
New-Volume -FileSystem NTFS -DriveLetter T -FriendlyName 'New-Volume' -AllocationUnitSize 1024KB
powershell
# 在win11下看下设定,此事在 "MSFT_ResiliencySetting" 中亦有记载
Get-CimInstance -Namespace "Root/Microsoft/Windows/Storage" -ClassName "MSFT_ResiliencySetting"
ObjectId : {1}\\DEVELOPER\root/Microsoft/Windows/Storage/Providers_v2\SPACES_ResiliencySetting.Obj
ectId="{28363463-ed42-11ed-ba85-806e6f6e6963}:RS:{28363462-ed42-11ed-ba85-806e6f6e6963}
{00000000-0000-0000-0000-000000000000}"
PassThroughClass :
PassThroughIds :
PassThroughNamespace :
PassThroughServer :
UniqueId : {28363462-ed42-11ed-ba85-806e6f6e6963}{00000000-0000-0000-0000-000000000000}
Description : 将数据存储在物理磁盘带区上,从而最大限度提高容量和吞吐量,但是会降低可靠性。这种存储数
据布局至少需要一个磁盘,并且无法防止发生磁盘故障。
InterleaveDefault : 262144
InterleaveMax : 16777216
InterleaveMin : 16384
Name : Simple
NumberOfColumnsDefault : 65535
NumberOfColumnsMax : 128
NumberOfColumnsMin : 1
NumberOfDataCopiesDefault : 1
NumberOfDataCopiesMax : 1
NumberOfDataCopiesMin : 1
NumberOfGroupsDefault : 1
NumberOfGroupsMax : 1
NumberOfGroupsMin : 1
ParityLayout :
PhysicalDiskRedundancyDefault : 0
PhysicalDiskRedundancyMax : 0
PhysicalDiskRedundancyMin : 0
RequestNoSinglePointOfFailure : False
PSComputerName :
ObjectId : {1}\\DEVELOPER\root/Microsoft/Windows/Storage/Providers_v2\SPACES_ResiliencySetting.Obj
ectId="{28363463-ed42-11ed-ba85-806e6f6e6963}:RS:{28363462-ed42-11ed-ba85-806e6f6e6963}
{00000000-0000-0000-0100-000000000000}"
PassThroughClass :
PassThroughIds :
PassThroughNamespace :
PassThroughServer :
UniqueId : {28363462-ed42-11ed-ba85-806e6f6e6963}{00000000-0000-0000-0100-000000000000}
Description : 将数据存储在物理磁盘带区上,以便创建两个或三个数据副本。这样可提高可靠性,但是会降低容
量。为了防止单磁盘故障,请至少使用两个磁盘(如果你使用的是群集,则至少使用三个磁盘);为
了防止双磁盘故障,请至少使用五个磁盘。
InterleaveDefault : 262144
InterleaveMax : 16777216
InterleaveMin : 16384
Name : Mirror
NumberOfColumnsDefault : 65535
NumberOfColumnsMax : 128
NumberOfColumnsMin : 1
NumberOfDataCopiesDefault : 2
NumberOfDataCopiesMax : 8
NumberOfDataCopiesMin : 1
NumberOfGroupsDefault : 1
NumberOfGroupsMax : 1
NumberOfGroupsMin : 1
ParityLayout :
PhysicalDiskRedundancyDefault : 1
PhysicalDiskRedundancyMax : 7
PhysicalDiskRedundancyMin : 0
RequestNoSinglePointOfFailure : True
PSComputerName :
ObjectId : {1}\\DEVELOPER\root/Microsoft/Windows/Storage/Providers_v2\SPACES_ResiliencySetting.Obj
ectId="{28363463-ed42-11ed-ba85-806e6f6e6963}:RS:{28363462-ed42-11ed-ba85-806e6f6e6963}
{00000000-0000-0000-0200-000000000000}"
PassThroughClass :
PassThroughIds :
PassThroughNamespace :
PassThroughServer :
UniqueId : {28363462-ed42-11ed-ba85-806e6f6e6963}{00000000-0000-0000-0200-000000000000}
Description : 将数据和奇偶校验信息存储在物理磁盘带区上,这样可提高可靠性,但是会略微降低容量和性能。
为了防止单磁盘故障,请至少使用三个磁盘;为了防止双磁盘故障,请至少使用七个磁盘。
InterleaveDefault : 262144
InterleaveMax : 16777216
InterleaveMin : 16384
Name : Parity
NumberOfColumnsDefault : 65535
NumberOfColumnsMax : 8
NumberOfColumnsMin : 3
NumberOfDataCopiesDefault : 1
NumberOfDataCopiesMax : 8
NumberOfDataCopiesMin : 1
NumberOfGroupsDefault : 65535
NumberOfGroupsMax : 1
NumberOfGroupsMin : 1
ParityLayout : 2
PhysicalDiskRedundancyDefault : 1
PhysicalDiskRedundancyMax : 2
PhysicalDiskRedundancyMin : 1
RequestNoSinglePointOfFailure : True
PSComputerName
分层存储
参考资料
存储空间可以使用两种基本技术为数据提供容错:镜像和奇偶校验。 在存储空间直通中,ReFS 引入了镜像加速奇偶校验,允许创建同时使用镜像和奇偶校验弹性的卷。 镜像加速奇偶校验在不牺牲性能的情况下提供了低廉、高空间效率的存储
由于是混合阵列且 SSD 数量少,因此肯定是要做一些小小的区别的。需要 SSD 做镜像、HDD 做奇偶校验(没有支持 SSD 的高性能奇偶校验阵列方式)。正好 win 就支持这么个神奇阵列,镜像加速奇偶校验
这个功能是 winserver2016 新加入的,是 ReFS 的独有功能。简言之就是数据先写入镜像模式 SSD,然后再慢慢旋转到奇偶校验的机械阵列上去
这个功能非常的有趣,实测也可以给出来非常好看的性能,毕竟做缓存的是两个 8T 的 4.0 磁盘。镜像后也有约 2T 的缓存空间,非常难写爆
功能说明
微软官方推荐镜像、奇偶比例 28 开
写入行为非常的简单,简单概括下就两点
- 镜像有空间:在镜像写入或修改
- 镜像没空间:在奇偶写入或修改
官方备注:
- 读取永远不会导致 ReFS 将数据旋转回镜像层。从奇偶校验读取时不会对性能产生实际负面影响,如果镜像和奇偶校验是用相同媒体类型构建的,则读取性能将相当
- 写入镜像层 > 重新分配的写入 >> 奇偶校验层。可以配置 ReFS 的压缩功能,这大大提高了超过 90% 空间已满的镜像加速奇偶校验卷的性能
- 镜像达到指定容量阈值后,ReFS 就开始旋转数据。此旋转阈值的值越高,ReFS 在镜像层中保留数据的时间就越长。 将热数据留在镜像层对性能来说是最佳的,但 ReFS 将无法有效地提供大量传入 IO。如果未设置此注册表项,ReFS 将使用默认值 85%。 对于大多数部署,建议使用此默认值,且不建议使用低于 50% 的值
配置
按照官方的推荐容量比例进行评估,完整的阵列需要 2x16t 的固态来缓存,不过条件有限,两个 4t 的 9a3 差不多得了。测试阵列按照机械可用容量 550g 计算,奇偶 2100G,需要 550g 的镜像空间
弱智 gui、必须命令行创建阵列
注意!0car 是我设定的卷名,test 是存储池名,数据副本数和列数要按照实际磁盘数量计算
powershell
# 查看存储分层命名。我这里创建好了
Get-StorageTier | Select FriendlyName, ResiliencySettingName, PhysicalDiskRedundancy
FriendlyName ResiliencySettingName PhysicalDiskRedundancy
------------ --------------------- ----------------------
MirrorOnSSD Mirror 1
ParityOnHDD Parity 2
powershell
# 删除错误的分层,如果存在
Remove-StorageTier -FriendlyName "ParityOnHDD"
Remove-StorageTier -FriendlyName "MirrorOnSSD"
# 创建存储分层
New-StorageTier -StoragePoolFriendlyName "Test" -FriendlyName ParityOnHDD -ResiliencySettingName Parity -MediaType HDD -PhysicalDiskRedundancy 2 -NumberOfColumns 6
New-StorageTier -StoragePoolFriendlyName "Test" -FriendlyName MirrorOnSSD -ResiliencySettingName Mirror -MediaType SSD -NumberOfDataCopies 2
# 这里有个逆天的地方,镜像加速奇偶校验似乎不需要设置分层的交错大小,严格设置后的表现反而鶸了
# 创建虚拟磁盘
New-Volume -DriveLetter O -FriendlyName "0car" -FileSystem ReFS -StoragePoolFriendlyName "Test" -StorageTierFriendlyNames MirrorOnSSD, ParityOnHDD -StorageTierSizes 55GB, 2100GB -AllocationUnitSize 64KB
# 调整旋转主动性
Set-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Policies -Name DataDestageSsdFillRatioThreshold -Value 75
测试
使用 ufwtest 进行测试,预期是共享 4.0x4 的南桥的 9a3 最大写入速度 2-3G,缓外写入速度差不多 160M(我承认有先射箭再靶子的成分,不过本来的预测也是这个)。测试用盘我设置的 SSD 空间只有 55G,方便快速出缓,但是缓外的效果非常的离谱
首先最离谱的是似乎镜像加速奇偶校验似乎不需要设置分层的交错大小,严格设置成 16KB 和 32KB 后缓外写入速度居然从 160MB 降低到了 130MB(?????),GC 时的波动也是类似。看起来应该是旋转过程中实现了条带写(先写镜像再条带转移到奇偶)
其次是这玩意和 SSD 一样,也有 GC(从 Prometheus 上看,此时又读又写,应该是镜像层转移到奇偶层),当前配置 4 分钟就会 GC 一次,GC 的时候写入速度只有 30MB/S
而这个 GC 更是个几把,55G 的小缓存还好,每隔 4 分钟 GC 一次,我设置了 1T 的缓存之后完全就是三秒真男人清空缓存,然后立马太监,调整了旋转主动性到 64% 都没用,不论怎么样都是 4 分钟 GC 一次。不过大缓存的吞吐量应该是大了非常多,看监控差不多线速 500MB/s。最终均速 588MB/S
所以小小缩放一下,生产阵列这么优化之后峰值写入 5G 左右,Cache 3.2T,持续写入速度 1G。算是勉强够用?但是这个峰值速度存在巨大的问题,比如说像是网络下载,明明资源能跑满 2.5g 下载速度的,却因为爆缓存了所以只能跑 60M 的下载速度(还是读写混合)。得 fio 测试下限制带宽的写入速度怎么样
不知道为什么,从 100g 的 sriov 网卡互访 kswap 会爆,所以该用走 2.5g 网卡的读写测试。实测写了 500G 出缓之后依然保持 285M 的写入速度(同时开了个读取,但是只能跑到 30M)。所以如果写入速度不超过转移速度,GC 时就不会掉到 30M 的那么狠
测试了一下就这样吧,只能说海星。勉强达到希望热数据能利用镜像 SSD,缓外能有机械条带写速的预期。虽然理论上分区管理,4 盘 4.0U2 做镜像更劲爆,但是按照之前的测试经验,我只能说太麻烦了。现在合成一个 67T 的 SSHD,cache 有 500G 也说得过去,剩下的两个 CD6 直接做一个 15T 的 r0 存热数据更爽
bash
fio --name=readwrite_test --ioengine=libaio --rate=500M --rw=randwrite --size=1T --numjobs=1 --blocksize=1M --runtime=60m --directory=/mnt/0car
readwrite_test: (g=0): rw=randwrite, bs=(R) 1024KiB-1024KiB, (W) 1024KiB-1024KiB, (T) 1024KiB-1024KiB, ioengine=libaio, iodepth=1
fio-3.36
Starting 1 process
Jobs: 1 (f=1), 0B/s-500MiB/s: [f(1)][100.0%][eta 00m:00s]
readwrite_test: (groupid=0, jobs=1): err= 0: pid=1389934: Thu Jan 2 09:24:13 2025
write: IOPS=226, BW=227MiB/s (238MB/s)(798GiB/3600019msec); 0 zone resets
slat (usec): min=277, max=823905, avg=4397.29, stdev=5840.10
clat (nsec): min=590, max=186957k, avg=3354.19, stdev=261579.00
lat (usec): min=277, max=998480, avg=4400.64, stdev=5902.31
clat percentiles (nsec):
| 1.00th=[ 660], 5.00th=[ 732], 10.00th=[ 788], 20.00th=[ 908],
| 30.00th=[ 1064], 40.00th=[ 1288], 50.00th=[ 1688], 60.00th=[ 2480],
| 70.00th=[ 3504], 80.00th=[ 4896], 90.00th=[ 6560], 95.00th=[ 7776],
| 99.00th=[13120], 99.50th=[16768], 99.90th=[30848], 99.95th=[38656],
| 99.99th=[64768]
bw ( KiB/s): min= 3840, max=888832, per=100.00%, avg=232674.45, stdev=96269.89, samples=7191
iops : min= 3, max= 868, avg=227.18, stdev=94.00, samples=7191
lat (nsec) : 750=6.23%, 1000=19.74%
lat (usec) : 2=28.33%, 4=19.67%, 10=23.94%, 20=1.79%, 50=0.28%
lat (usec) : 100=0.01%, 250=0.01%, 500=0.01%, 750=0.01%
lat (msec) : 10=0.01%, 250=0.01%
cpu : usr=0.26%, sys=12.20%, ctx=368445, majf=33, minf=64
IO depths : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0%
submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
issued rwts: total=0,816965,0,0 short=0,0,0,0 dropped=0,0,0,0
latency : target=0, window=0, percentile=100.00%, depth=1
Run status group 0 (all jobs):
WRITE: bw=227MiB/s (238MB/s), 227MiB/s-227MiB/s (238MB/s-238MB/s), io=798GiB (857GB), run=3600019-3600019msec