香港服务器运行Debian 10时,如何配置XFS文件系统和RAID阵列,提升大数据应用的存储吞吐量?

凌晨 02:10,我捧着半杯凉掉的美式,站在香港葵涌机房第 3 排第 6 柜前。客户的 ClickHouse 集群白天刚顶了一波高峰,单节点存储吞吐被打爆,业务侧拼命做分片,可单机 I/O 还是瓶颈。这篇文章,是那一夜我在香港机房里,从盘位摸到文件系统的全过程——踩过的坑、做过的测试、最后稳定落地的参数,一条龙交给你。新手能照做,老手也能抠细节。
目标与场景
目标:在 Debian 10(Buster)上,为大数据/分析型负载(ClickHouse / Kafka / HDFS DataNode)构建高吞吐、可恢复的本地存储卷。
方案:基于 mdadm 实现 RAID(以 RAID10 为主),文件系统选 XFS,结合条带(stripe)与挂载参数调优。
负载特征:顺序读写为主(批处理、合并、后台压缩),夹杂中等随机写(写放大和小文件元数据压力)。
实机参数与环境(现场记录)
| 项目 | 配置 |
|---|---|
| 机型 | 2U 通用服务器 |
| CPU | 2 × Intel Xeon Silver(共 24 物理核) |
| 内存 | 256 GB DDR4 |
| HBA | LSI 9300-8i(IT 模式) |
| 数据盘 | 8 × 4TB 7200RPM 企业级 SATA(带 RV 传感器) |
| 系统盘 | 2 × 960GB SATA SSD(做 RAID1 给 OS) |
| 额外 NVMe(可选) | 1 × 1.6TB NVMe(预留 XFS 外置日志/冷热分层) |
| OS | Debian 10 (Buster) 4.19 内核 |
| 关键包 | mdadm、xfsprogs、smartmontools、fio |
落地策略:
- ClickHouse 节点:本地卷需要高顺序吞吐与较快重建 → RAID10(near layout)+ XFS。
- Kafka 节点:也常用 RAID10;若追求极致性能且上层多副本,有时会用 JBOD/RAID0(此文以 RAID10 为主线)。
- HDFS DataNode:若上层三副本且强依赖分布式冗余,JBOD 亦可,但本文围绕标题选 RAID10。
RAID 选择与条带计算
RAID 方案取舍(实话实说)
| 方案 | 顺序吞吐 | 随机读写 | 重建风险 | 容量利用 | 适用 |
|---|---|---|---|---|---|
| RAID0 | 极高 | 一般 | 极高(坏一块全毁) | 100% | 上层多副本、临时盘 |
| RAID5/6 | 高 | 一般 | 重建时间长,穿越读风险 | 75%/66% | 容量敏感但可容忍重建风险 |
| RAID10 | 高 | 好 | 较低(重建快) | 50% | 分析/日志/OLAP 节点首选 |
本次落地:8 盘做 RAID10,--layout=n2(near),--chunk=512K。
- 可并发数据盘数 sw = N / copies = 8/2 = 4
- XFS stripe 建议:su = 512K,sw = 4(下面 mkfs 会用)。
上线前健康检查与对齐
磁盘健康(别嫌麻烦,救命用)
apt-get update && apt-get install -y smartmontools
for d in /dev/sd[b-i]; do
smartctl -a $d | egrep "Model|Serial|Power_On_Hours|Reallocated|Pending"
done
有不健康的盘,先换;别带病上线。
分区对齐(1MiB 对齐,避免写放大)
apt-get install -y parted
for d in /dev/sd[b-i]; do
parted -s $d mklabel gpt
parted -s -a optimal $d mkpart data 1MiB 100%
done
用 mdadm 创建 RAID10
apt-get install -y mdadm
# 创建 RAID10(near=2,条带 512K)
mdadm --create /dev/md0 \
--level=10 \
--raid-devices=8 \
--chunk=512 \
--layout=n2 \
/dev/sdb1 /dev/sdc1 /dev/sdd1 /dev/sde1 /dev/sdf1 /dev/sdg1 /dev/sdh1 /dev/sdi1
# 查看进度
watch -n 3 cat /proc/mdstat
经验:创建时就把 chunk 定好,后续改代价大。条带越大,顺序吞吐越好(但小 I/O 可能浪费)。
阵列持久化(否则重启可能丢阵列名):
mdadm --detail --scan | tee -a /etc/mdadm/mdadm.conf
update-initramfs -u
在 RAID 上创建 XFS(带几项关键参数)
参数思路(给老司机看)
- -d su=512k,sw=4:和上面 RAID10 条带匹配,减少跨带写放大。
- -l size=1024m:放大日志区,缓解高并发写入下的元数据堵塞。
- -n ftype=1:目录项携带文件类型(容器/OverlayFS 友好)。
- -m reflink=0:关闭 CoW(大顺序写为主的 OLAP/Kafka 通常不需要 CoW;如需快照/去重再考虑开)。
apt-get install -y xfsprogs
mkfs.xfs -f \
-d su=512k,sw=4 \
-l size=1024m \
-n ftype=1 \
-m reflink=0 \
/dev/md0
验证 stripe 信息(注意单位:sunit/swidth 是以 4K 块为单位的):
xfs_info /dev/md0
# 期望看到:sunit=128 (512K/4K),swidth=512 (128*4),与上面 su/sw 对应
挂载与 fstab(稳字当头)
挂载选项(顺序吞吐优先、安全第一):
- noatime:减少 atime 写入。
- inode64:海量 inode 时表现更好。
- logbufs=8,logbsize=256k:加大日志缓冲,提高并发。
- 不要随便 nobarrier(断电风险)。有 UPS + 带电容/BBU 的控制器再评估。
- SSD 才考虑 discard(或用周期性 fstrim)。
mkdir -p /data
echo "/dev/md0 /data xfs defaults,noatime,inode64,logbufs=8,logbsize=256k 0 0" >> /etc/fstab
mount -a && df -h /data
可选:把 NVMe 做 外置日志(元数据压力极大时有效)
# 假设 /dev/nvme0n1p1 预留 10-20G 做 logdev
mkfs.xfs -f -d su=512k,sw=4 -l logdev=/dev/nvme0n1p1,size=2048m -n ftype=1 -m reflink=0 /dev/md0
mount -o logdev=/dev/nvme0n1p1 /dev/md0 /data
系统级调优(少量参数,收益可观)
读提前(readahead):顺序读明显增益
# 底层物理盘与阵列都设大点,例如 64MB
blockdev --setra 131072 /dev/md0
for d in /dev/sd[b-i]; do blockdev --setra 131072 $d; done
I/O 调度器
- SATA/SAS:mq-deadline 一般靠谱;
- NVMe:默认 none,保持即可。
查看/设置(示例):
cat /sys/block/md0/queue/scheduler
echo mq-deadline > /sys/block/md0/queue/scheduler
脏页回刷(降低写峰值卡顿)
cat <<EOF >> /etc/sysctl.d/99-io-tuning.conf
vm.dirty_background_ratio=5
vm.dirty_ratio=10
EOF
sysctl --system
md 重建速度(生产窗口谨慎)
echo 50000 > /proc/sys/dev/raid/speed_limit_max # 单位 KB/s
echo 20000 > /proc/sys/dev/raid/speed_limit_min
基准测试(fio 模板与指标)
测试目录:/data;务必避开业务高峰,确保 direct=1 直写。
顺序写(1MiB,4 并发,100G)
fio -name=seqwrite -directory=/data -numjobs=4 -ioengine=libaio \
-rw=write -bs=1M -size=100G -iodepth=32 -direct=1 -group_reporting
顺序读
fio -name=seqread -directory=/data -numjobs=4 -ioengine=libaio \
-rw=read -bs=1M -size=100G -iodepth=32 -direct=1 -group_reporting
混合读写(70/30)
fio -name=mix -directory=/data -numjobs=8 -ioengine=libaio \
-rw=randrw -rwmixread=70 -bs=128k -size=50G -iodepth=64 -direct=1 -group_reporting
现场实测(代表值,仅供参考)
| 场景 | 参数 | 吞吐 / IOPS(平均) | 延迟(平均) |
|---|---|---|---|
| 顺序写 | 1M × 4 并发 | 0.85–1.0 GB/s | 8–12 ms |
| 顺序读 | 1M × 4 并发 | 1.3–1.6 GB/s | 5–9 ms |
| 混合 70/30 | 128k × 8 并发 | 450–650 MB/s / 3–6k IOPS | 6–15 ms |
注:chunk=1M 也测过,顺序读略高、混合随机略降;考虑业务占比,最终定 512K。
XFS 细节再抠一点(给发烧友)
AG(Allocation Group):并行元数据与并发分配更强。大盘可适当增加 agcount,例如 16:
mkfs.xfs -f -d su=512k,sw=4,agcount=16 -l size=1024m -n ftype=1 -m reflink=0 /dev/md0
预分配:大文件写入前 fallocate,减少碎片:
fallocate -l 200G /data/ch_data_part.bin
在线扩容:新增盘 → 扩 RAID → xfs_growfs /data(XFS 不能收缩,先规划再上)。
应用层落地建议
ClickHouse
storage_configuration 中把大表落在 /data,后台合并高峰期避免和备份/TTL 清理撞车。
max_bytes_before_external_group_by/sort 根据内存调,降低落盘风暴。
周期性 fstrim(如果底层含 SSD/NVMe 分层)。
Kafka
log.dirs=/data/kafka-logs;多个卷时每盘一个目录,避免单卷热区。
num.recovery.threads.per.data.dir 与 CPU/盘数匹配;flush.messages 不要过激。
log.segment.bytes 合理增大,减少 segment 切换的元数据开销。
HDFS DataNode
RAID10 与 JBOD 二选一。若已选 RAID10,DataNode 配置中把卷识别清楚;namenode 端副本策略按机架/节点分散,规避同阵列双副本。
我踩过的坑(以及我怎么解)
创建完阵列重启进了 initramfs
原因:/etc/mdadm/mdadm.conf 没写入阵列信息。
解决:mdadm --detail --scan >> /etc/mdadm/mdadm.conf && update-initramfs -u,再重启。
顺序写吞吐忽上忽下
排查:iostat -x 1 看到个别盘 await 飙高,smartctl 显示写错误重试。
处理:先下线该盘 mdadm /dev/md0 -f /dev/sdx1,更换热备补回;恢复后吞吐稳定。
xfs 日志打满,应用写阻塞
现象:dmesg 里 XFS: busy 相关提示。
处理:-l size=1024m 放大日志,并用 logbufs=8,logbsize=256k;高峰错峰合并/压缩任务。
对齐没做好,fio 秒数奇长
经验:务必用 parted -a optimal,并核对 xfs_info 的 sunit/swidth 与 RAID 条带一致。
重建时业务抖动明显
做法:在业务低谷提重建限速,在高峰调低:
echo 100000 > /proc/sys/dev/raid/speed_limit_max
# 高峰期再降到 20000-50000
误开 nobarrier 导致断电后元数据损伤
反思:除非有可靠的写缓存保护(BBU/超容电容)+ UPS,不要开。
一键回看:关键命令清单
# 健康检查
apt-get install -y smartmontools mdadm xfsprogs fio parted
for d in /dev/sd[b-i]; do smartctl -a $d; done
# 分区对齐
for d in /dev/sd[b-i]; do
parted -s $d mklabel gpt
parted -s -a optimal $d mkpart data 1MiB 100%
done
# 创建 RAID10
mdadm --create /dev/md0 --level=10 --raid-devices=8 --chunk=512 --layout=n2 /dev/sd[b-i]1
mdadm --detail --scan | tee -a /etc/mdadm/mdadm.conf
update-initramfs -u
# XFS(条带匹配 + 大日志)
mkfs.xfs -f -d su=512k,sw=4 -l size=1024m -n ftype=1 -m reflink=0 /dev/md0
mkdir -p /data
echo "/dev/md0 /data xfs defaults,noatime,inode64,logbufs=8,logbsize=256k 0 0" >> /etc/fstab
mount -a
# 调优
blockdev --setra 131072 /dev/md0; for d in /dev/sd[b-i]; do blockdev --setra 131072 $d; done
echo mq-deadline > /sys/block/md0/queue/scheduler
sysctl -w vm.dirty_background_ratio=5 vm.dirty_ratio=10
# 验证
xfs_info /dev/md0
fio ... # 如上模板
结果回顾(对比表)
| 设置 | 顺序读 | 顺序写 | 备注 |
|---|---|---|---|
| RAID10 + XFS 默认 | 1.1–1.3 GB/s | 0.7–0.8 GB/s | 无条带匹配 |
| RAID10(chunk=512K)+ XFS(su=512K, sw=4) | 1.3–1.6 GB/s | 0.85–1.0 GB/s | 主要收益来自条带对齐 |
| + readahead 64MB、logbufs 调优 | 1.4–1.6+ GB/s | 0.9–1.0 GB/s | 抖动更小 |
从机房出来,风还在吹
早上 05:40,机房外的海风顺着货柜码头吹过来。我重新看了一眼监控面板,吞吐在高位稳得像钉在墙上的水准仪。客户白天再冲一波,我们的 I/O 也不再虚。存储这活儿,终究是工程:从盘到阵列,从条带到文件系统,从参数到上线窗口,每一步都能决定最终的业务体验。
如果你也在香港的某个机柜前纠结 RAID 和 XFS 的参数,希望这篇实战给你一点确定性:
- 把条带算明白,把日志配大,把回刷和读提前调顺,再用你自己的数据去测。
- 别怕深夜,那是运维最清醒的时候。
附:通用故障速查
- 阵列掉盘:mdadm --detail /dev/md0 + dmesg → 标记失败盘并更换 -f / -r。
- XFS 异常关机后检查:xfs_repair -n /dev/md0(只读检查),有需要再去掉 -n。
- 空间不足又想扩容:先物理加盘 → 扩 RAID → xfs_growfs /data。
- 长期维护:mdadm --monitor --scan --daemonise --syslog,配合报警系统。
- 有问题就按这套落地跑一遍,你会发现:不是玄学,是细节。