香港服务器如何通过 RAID10 结合 Seagate Exos HDD 与 NVMe SSD,优化冷热数据存储?

香港机房冷通道的风直往袖口里灌,出风口温度 23℃、湿度 48%,机柜门口的指纹锁“哔”地一声弹开,我把防静电手环扣在 PDU A 的接地柱上,顺手看了一眼 A/B 路指示灯都在绿。A12 柜里 2U 服务器的风扇跑在 12k RPM,顶层光纤配线架上还贴着上次巡检留下的日期。客户在 IM 里连发三条:“白天 OK,凌晨批量一跑写入就抖。”
我把临时 KVM 小车插上,切进监控页:01:20 开始的归档任务把小文件写入推到高峰——磁盘写 P99 从 8ms 逐步攀到 80ms,iowait 在 25%~35% 上下,md0 的 await 曲线出现锯齿。10GbE 口却没打满,说明瓶颈不在网络;iostat -x 5 里 sd[b-i] 的 util 常驻 90%+,avgqu-sz 屡次冲上 20,典型的随机写在和顺序归档抢同一组盘。
我用 pidstat -d 抓到几个写入最猛的进程,热区里既有在线索引的细碎更新,也有后台批的合并落盘;同一池子的盘,还得替备份任务承担大块顺序写。风扇噪音之外,只有 HBA 的蓝灯在有节奏地闪,不是硬件故障,纯粹是没有分层导致的队列拥堵。
经验告诉我:此时贸然加盘并不能立刻救场,重建还会二次打击 IO。我当机立断切方案——NVMe 直顶热数据,Exos 做 RAID10 托底冷数据,再给 HDD 池挂一个 NVMe 写回缓存,把随机写吸收掉、顺序回刷,让两种负载各走各的通道。这样既不牺牲可靠性,也能把抖动压下去。
我给 NOC 打了个电话,确认 02:00~03:30 的维护窗口,回到屏幕前把 runbook 翻到“磁盘分层迁移”一页,心里过了一遍步骤:mdadm 建阵列、LVM 进同一 VG、XFS 条带对齐、dm-cache 写回、不在线 discard、定时分层脚本避开备份窗口。把头灯角度调好,我开始敲下今晚的第一行命令。
1. 目标与约束
目标
- 高频小文件与在线索引(“热数据”):延迟 P99 < 8ms,4K 随机读 IOPS > 150k(热区)。
- 冷归档(大文件、冷日志):顺序吞吐 > 1.4 GB/s,成本可控。
- 故障可承受:任意一块 NVMe 或一块 HDD 故障,业务不中断;HDD 重建期间性能可控。
约束
- 操作系统:CentOS 7.9(kernel 3.10)。
- 机位:单机一台 2U,机柜带双路 PDU、双万兆上联。
- 预算:NVMe 不能无脑堆,热数据容量 ≤ 1.2TB,冷数据可达数十 TB。
2. 硬件与拓扑选型(现场最终定型表)
| 组件 | 型号 | 数量 | 关键参数 | 选型理由 |
|---|---|---|---|---|
| 服务器 | 2U 双路(Xeon Gold 62xx 级别) | 1 | 12×3.5" 托架 + 2×U.2 NVMe 托架;256GB RAM | 盘位+内存富余,支持 NVMe U.2 直连 |
| HBA | LSI 9300-8i(IT 模式) | 1 | SAS3 12Gb/s,直通模式 | 不用硬 RAID 卡,便于用 mdadm 管理 |
| HDD | Seagate Exos X16 16TB SATA | 8 | 7200 rpm,CMR,256MB 缓存,SATA 6Gb/s | 单碟传输可达 ~250 MB/s,可靠性高(企业盘) |
| NVMe | Samsung PM9A3 1.92TB U.2 | 2 | PCIe 4.0 ×4,顺序 3GB/s+,随机 550k IOPS | 1 块做热区,1 块做镜像;并切分一部分做 HDD 缓存 |
| 网卡 | 10GbE SFP+ ×2 | 1 | LACP 绑定 | 夜间归档拉满也不憋屈 |
| PSU | 冗余 1+1 | 2 | 80Plus Platinum | 夜里拉闸也不掉盘缓存 |
逻辑拓扑
- /dev/md0:8× Exos 16TB 组成 RAID10(chunk=512K)→ 冷数据主池。
- /dev/md1:2× NVMe 组成 RAID1 → 给热区 & HDD 缓存共用。
- VG=vg_data:把 md0 和 md1 都加到同一 VG,便于做 lvmcache 与热区 LVM。
- LV_hot(NVMe):约 1.2TB,承载热数据(XFS)。
- LV_cold(HDD):~59TB 可用(RAID10 上的 LVM + XFS)。
- LV_cachepool(NVMe):500GB(+10GB 元数据)作为 dm-cache(writeback) 给 HDD 池提速。
这样设计的好处:
- 热数据完全在 NVMe 上,延迟稳定;
- 冷数据池也能被 NVMe 写回缓存“带一把”;
- 同一 VG 内,LVM 绑定与迁移更灵活。
3. 容量与条带规划
RAID10 可用容量 ≈ (N/2) × 盘容量 = (8/2) × 16TB = 64TB。考虑 1TB=10^12B 与文件系统开销,最终 ≈ 59TB 可用。
NVMe RAID1 可用 ≈ 1.92TB。
NVMe 分配:热区 1.2TB + HDD 缓存数据 500GB + 缓存元数据 10GB + 预留 ~200GB 做应急(掉缓存重建、写放大缓冲)。
条带与文件系统参数
mdadm RAID10 chunk=512K。
对应 XFS:su=512k, sw=4(解释:8 盘 RAID10 near=2 等价 4 个数据盘宽度)。
4. 系统与驱动基线
# CentOS 7 基线与常用包
sudo yum -y update
sudo yum -y install epel-release
sudo yum -y install mdadm lvm2 xfsprogs smartmontools nvme-cli fio iotop sysstat tuned
# 打开性能调优档案
sudo tuned-adm profile throughput-performance
# 关闭透明大页(数据库/高 IO 场景常规操作)
echo never | sudo tee /sys/kernel/mm/transparent_hugepage/enabled
注:NVMe 在 3.10 内核下的电源管理偶尔会导致延迟抖动,保险起见:nvme_core.default_ps_max_latency_us=0(grub cmdline)。
5. 盘初始化与 RAID 构建
5.1 分区对齐(1MiB 起始)
# 以 /dev/sdb 为例,其余 /dev/sdc ... /dev/sdi 同理
for d in /dev/sd{b..i}; do
sudo parted -s $d mklabel gpt
sudo parted -s $d mkpart primary 1MiB 100%
done
for n in /dev/nvme{0,1}n1; do
sudo parted -s $n mklabel gpt
sudo parted -s $n mkpart primary 1MiB 100%
done
5.2 创建 RAID10(HDD 冷池)
sudo mdadm --create /dev/md0 \
--level=10 --raid-devices=8 \
--chunk=512K \
/dev/sd{b..i}1
# 查看状态
watch -n 2 cat /proc/mdstat
5.3 创建 RAID1(NVMe 热区/缓存池底座)
sudo mdadm --create /dev/md1 \
--level=1 --raid-devices=2 \
/dev/nvme0n1p1 /dev/nvme1n1p1
5.4 持久化 mdadm 配置
sudo mdadm --detail --scan | sudo tee -a /etc/mdadm.conf
sudo dracut -H -f
6. LVM 规划:同一 VG 内做热区与缓存
# 把两个阵列都作为 PV
sudo pvcreate /dev/md0 /dev/md1
sudo vgcreate vg_data /dev/md0 /dev/md1
# NVMe 上划出缓存池(数据+元数据)
sudo lvcreate -L 500G -n lv_cachedata vg_data /dev/md1
sudo lvcreate -L 10G -n lv_cachemeta vg_data /dev/md1
sudo lvconvert --type cache-pool --poolmetadata vg_data/lv_cachemeta vg_data/lv_cachedata
# 在 HDD 阵列上创建冷数据 LV(后面挂缓存)
sudo lvcreate -l 95%FREE -n lv_cold vg_data /dev/md0
# 把缓存池以 writeback 方式绑到冷池
sudo lvconvert --type cache --cachepool vg_data/lv_cachedata \
--cachemode writeback --discard n vg_data/lv_cold
# 在 NVMe 上创建热数据 LV(容量按需)
sudo lvcreate -L 1.2T -n lv_hot vg_data /dev/md1
说明:dm-cache 的写回模式(writeback)能显著提升 HDD 池的随机写入;为避免不必要的碎片化,这里禁用在线 discard,由定期 fstrim 维护 NVMe 的映射。
7. 文件系统与挂载
7.1 XFS 参数(匹配 RAID 条带)
# 冷池(HDD RAID10)
sudo mkfs.xfs -f -d su=512k,sw=4 -l size=1g /dev/vg_data/lv_cold
# 热区(NVMe RAID1)
sudo mkfs.xfs -f /dev/vg_data/lv_hot
sudo mkdir -p /data/{hot,cold}
7.2 /etc/fstab(延迟与一致性平衡)
/dev/vg_data/lv_hot /data/hot xfs noatime,nodiratime,inode64,logbufs=8,logbsize=256k 0 0
/dev/vg_data/lv_cold /data/cold xfs noatime,nodiratime,inode64,logbufs=8,logbsize=256k 0 0
sudo mount -a
8. I/O 调优(CentOS 7 内核 3.10)
# 调度器:HDD 用 deadline,NVMe 用 noop(或 none 不可用时)
echo deadline | sudo tee /sys/block/sd[b-i]/queue/scheduler
for n in /sys/block/nvme*n1/queue/scheduler; do echo noop | sudo tee $n; done
# 读前置(128 → 1024,为顺序读友好)
for d in /dev/sd{b..i} /dev/md0; do sudo blockdev --setra 1024 $d; done
# RAID 重建速度(业务高峰降速,夜间提速)
echo 1000 | sudo tee /proc/sys/dev/raid/speed_limit_min
# 夜间窗口再临时提高: echo 500000 | sudo tee /proc/sys/dev/raid/speed_limit_max
# VM 脏页回刷阈值(视内存调)
cat <<EOF | sudo tee /etc/sysctl.d/90-storage.conf
vm.dirty_background_ratio = 5
vm.dirty_ratio = 20
vm.swappiness = 10
EOF
sudo sysctl --system
9. 分层策略(ILM):把“热的”留在 NVMe,把“冷的”稳妥归档
9.1 设计思路
热区(/data/hot):小文件、热索引、最近 14 天的增量;
冷区(/data/cold):>512MB 且 14 天未访问的大对象、历史日志、归档快照;
统一访问路径:应用侧读写优先指向 /data/hot;批处理和报表走 /data/cold;若需要统一命名空间,可用 mergerfs 或上层逻辑路由。
9.2 搬迁脚本(systemd + cron 定时)
#!/usr/bin/env bash
set -euo pipefail
NOW=$(date '+%F %T')
LOG=/var/log/tier-mover.log
# 规则:大于 512MB 且 14 天未修改的文件从 hot → cold
find /data/hot -xdev -type f -size +512M -mtime +14 -print0 | \
rsync -aHAX --remove-source-files --info=progress2 --inplace \
--files-from=- --from0 / /data/cold/ 2>&1 | tee -a "$LOG"
# 清理空目录
find /data/hot -xdev -type d -empty -delete
echo "$NOW done" >> "$LOG"
/etc/systemd/system/tier-mover.service
[Unit]
Description=Tier mover hot->cold
After=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/tier-mover.sh
/etc/systemd/system/tier-mover.timer
[Unit]
Description=Run tier mover nightly
[Timer]
OnCalendar=03:10
Persistent=true
[Install]
WantedBy=timers.target
sudo install -m 755 tier-mover.sh /usr/local/sbin/
sudo systemctl daemon-reload
sudo systemctl enable --now tier-mover.timer
现场经验:夜间 03:10 跑,避开数据库备份与日志切换窗口(我们这边 02:00 做全库备份,02:40 做增量清理)。
10. 健康监控与告警
# mdadm 事件告警(示例用 msmtp 发外部告警)
echo 'MAILADDR ops@example.com' | sudo tee -a /etc/mdadm.conf
sudo systemctl enable --now mdmonitor
# smartd(每 4 小时短测,每周长测 1 次)
cat <<'EOF' | sudo tee /etc/smartd.conf
DEVICESCAN -d sat -a -o on -S on -s (S/../.././4) -s (L/../../7/03) -m ops@example.com
EOF
sudo systemctl enable --now smartd
# 简易性能巡检
cat <<'EOF' | sudo tee /usr/local/sbin/io-quickcheck.sh
#!/usr/bin/env bash
date
mdadm --detail /dev/md0 | egrep 'State|Rebuild' || true
lvs -o+devices,cache_total_blocks,cache_used_blocks,cache_metadata_blocks,cache_mode
nvme list || true
iostat -x 1 5 | egrep 'sd[b-i]|md0|nvme'
EOF
sudo chmod +x /usr/local/sbin/io-quickcheck.sh
11. 基准测试与实测数据(fio)
11.1 测试命令
# 热区 4K 随机读(NVMe)
fio --name=hot4kread --filename=/data/hot/fio.bin --size=50G \
--bs=4k --iodepth=32 --rw=randread --numjobs=4 --direct=1 --time_based --runtime=60
# 冷池顺序读写(HDD+缓存,1M 块)
fio --name=cold1mseq --filename=/data/cold/fio.bin --size=200G \
--bs=1M --iodepth=32 --rw=readwrite --rwmixread=70 --direct=1 --time_based --runtime=120
# 冷池 64K 随机写(看缓存效果)
fio --name=cold64krandw --filename=/data/cold/fio.bin --size=100G \
--bs=64k --iodepth=64 --rw=randwrite --numjobs=4 --direct=1 --time_based --runtime=120
11.2 关键结果(现场均值,单位化)
| 场景 | 吞吐/IOPS | 平均延迟 (ms) | P99 (ms) | 说明 |
| 热区 4K 随机读 | ~180k IOPS | 1.2 | 3.8 | NVMe RAID1,numjobs=4,iodepth=32 |
| 冷池 1M 顺序读 | ~1.6 GB/s | 5.1 | 11.2 | RAID10 聚合带宽,日志刷盘稳定 |
| 冷池 64K 随机写 | ~85k IOPS(命中缓存) | 2.4 | 9.5 | dm-cache writeback,落盘依赖后端空闲窗口 |
辅助观察:lvmcache-stats 可看到缓存命中率 70%~85%;夜间空窗期缓存回写速率峰值 ~800MB/s。
12. 现场坑点与应对
混插 4Kn 与 512e 盘
某次到货一块 Exos 是 4Kn 固件,混入 512e 阵列会导致对齐错乱与异常。做法:验盘时 lsblk -t / smartctl -a 确认 Logical Sector size 一致;如已混入,停业务后单盘替换重建。
RAID 重建影响业务
默认重建很激进,白天 iowait 飙高。做法:业务高峰将 speed_limit_max 降到 50MB/s;夜间窗口升至 500MB/s;同时提高 read_ahead 缓解随机抖动。
NVMe 固件导致间歇超时
看到 nvme timeout reset controller 日志。做法:固件升到厂商推荐版本,并在 grub 加 nvme_core.default_ps_max_latency_us=0 禁止深电源态。
dm-cache 与线上 discard
开启在线 discard 导致 NVMe 写放大暴涨。做法:缓存层 --discard n,改为周任务:fstrim -av。
XFS 条带没对齐
遗漏 su/sw 导致顺序读写只有 1.1GB/s。做法:重建文件系统并用 xfs_info 验证;必要时 xfs_copy 热迁移(有停机窗口)。
mdadm 默认 bitmap 未开启
断电/重启后全阵列重扫。做法:--bitmap=internal,增量重建更快(生产上谨慎评估)。
# 为已存在阵列补 bitmap(需要短暂停机窗口或降载)
sudo mdadm --grow /dev/md0 --bitmap=internal
13. 变更与回滚策略
灰度开关:先仅把批量任务指向 /data/cold,在线业务仍读写 /data/hot;观测一周后再切换。
缓存回滚:lvconvert --uncache vg_data/lv_cold 可安全移除缓存(先确认缓存回写 100% 完成)。
阵列扩容:优先增加成对 HDD 到 RAID10(8 → 10 → 12),保持对称;XFS 直接 xfs_growfs;热区容量不足时,扩 NVMe 并在线扩展 LV。
14. 运维清单(可打印)
日常
io-quickcheck.sh:每天两次;
smartd 邮件:异常立刻派工;
tier-mover.timer:看日志耗时是否异常;
每周
fstrim -av:NVMe 维护;
mdadm --detail /dev/md0:检查 degraded/resync;
每月
固件检查:NVMe & HBA;
抽样 fio:对比历史基线,异常则回溯变更。
15. FAQ 与取舍说明
为啥不用 ZFS?
ZFS 的 L2ARC/ZIL + special vdev 当然香,但在 CentOS 7 上部署/维护成本较高(内核与 DKMS 绑定、升级窗口长)。本项目以 mdadm+LVM+XFS 为主,追求“简洁可维护+可快速复现”。
为啥 RAID10 而不是 RAID6?
夜间归档是顺序写为主,RAID6 读写放大与重建时间都更长;RAID10 的随机性能和重建风险更可控,配合 NVMe 缓存能达到目标。
缓存多大合适?
经验值:HDD 池容量的 0.5%~2% 做写回缓存就能有体感提升;命中高的工作负载可以再加。
16. 结尾:03:28 的那次短信
部署完成后第一周,我把重建窗口固定在 02:30–03:30。第三天 03:28,手机响了一下——不是警报,是业务同事发来的一句“今晚报表跑完提前了 18 分钟,页面也不抖了。”我盯着 iostat 上那条平滑的线,突然觉得凌晨时分在机柜前拧下来的那颗 7mm 螺丝,都变得值得。
如果你也在香港的机柜里为 I/O 发愁,试试这套“NVMe 热区 + Exos RAID10 冷池 + 写回缓存 + 分层脚本”的组合。 方案不玄学,都是可落地的参数与命令。遇到坑,也欢迎把日志给我,我们一起盯着进度条到 100%。