香港服务器 ZFS 到底要不要上 SLOG / L2ARC?——数据库与文件服务的选型、部署与完整对比评测

凌晨 1:40,香港将军澳机房的客户抱怨“数据库提交卡顿、SMB 拷贝忽快忽慢”,我们准备把一台数据库节点和一台文件服务节点从 ext4/LVM 迁到 ZFS,顺带验证两个老生常谈的问题:
- ZFS 到底要不要配 SLOG(分离 ZIL 日志设备)?
- L2ARC(读缓存)值不值得上?
我不想拍脑袋,所以直接拉了两台候选服务器做对比测试,把能想到的典型场景都跑了一遍:本地数据库 OLTP、NFS 强制同步小文件写、SMB 海量小文件热读。数据不说谎。
测试环境与硬件配置(香港节点,机柜同列)
OS 我仍然选 CentOS 7.9(客户生产环境一致),ZFS 采用 OpenZFS 2.1.x(kmod) 分支,内核稳定、驱动成熟。
节点 A(数据库型候选)
- 机型:Dell PowerEdge R650xs
- CPU:2 × Intel Xeon Silver 4314(16C/32T ×2)
- 内存:256 GB DDR4-2933
- 系统盘:2 × SATA SSD(RAID1,系统/日志)
- 数据盘池(Pool-DB):6 × 3.84 TB 企业级 SATA SSD(三星 PM883),3 组镜像 vdev(RAID10 类)
- SLOG(候选):2 × Intel Optane P4801X 100 GB(镜像)
- L2ARC(候选):1 × Samsung PM9A3 1.92 TB NVMe
- 网卡:Mellanox ConnectX-4 Lx 25GbE × 2
- 用途:PostgreSQL、MySQL OLTP 压测;NFS 同步写验证
节点 B(文件服务型候选)
- 机型:Supermicro 2U(H12 系列)
- CPU:AMD EPYC 7302P(16C/32T)
- 内存:128 GB DDR4-3200
- 系统盘:2 × SATA SSD(RAID1)
- 数据盘池(Pool-FS):12 × 16 TB 7200 RPM SAS HDD,2 个 raidz2 vdev(6D+2P)
- Special vdev(元数据/小文件):2 × Samsung PM983 960 GB NVMe(镜像)
- SLOG(候选):2 × Intel Optane P1600X 118 GB(镜像)
- L2ARC(候选):1 × Samsung PM9A3 3.84 TB NVMe
- 网卡:Mellanox ConnectX-4 Lx 25GbE × 2
- 用途:SMB/NFS 文件服务;小文件/大文件混合读写
注 1:所有 HBA 设置为 IT 模式直通;ashift=12(4K 扇区对齐)。
注 2:测试 VLAN 与生产隔离,交换机端口统一 25GbE,MTU 9000(Jumbo Frame)。
ZFS 关键配置与创建命令
软件安装(CentOS 7)
# 1) 安装仓库与内核模块(生产请固定版本)
yum install -y epel-release
yum install -y zfs
# 2) 加载模块并开机自启动
modprobe zfs
echo zfs >> /etc/modules-load.d/zfs.conf
创建数据库池(Pool-DB)
# 3 组镜像 vdev(相当于 RAID10),适合 OLTP 的低延迟与并发
zpool create -f Pool-DB \
mirror /dev/disk/by-id/ssd1 /dev/disk/by-id/ssd2 \
mirror /dev/disk/by-id/ssd3 /dev/disk/by-id/ssd4 \
mirror /dev/disk/by-id/ssd5 /dev/disk/by-id/ssd6 \
-o ashift=12 -o autoreplace=on -o autotrim=on
# 数据集参数(数据库)
zfs create -o recordsize=16K -o atime=off -o compression=lz4 \
-o primarycache=all -o logbias=latency Pool-DB/pgdata
创建文件服务池(Pool-FS)
# 2 个 raidz2 vdev(6+2),容量与容错兼顾;special vdev 存放元数据/小文件
zpool create -f Pool-FS \
raidz2 /dev/disk/by-id/hdd1 ... /dev/disk/by-id/hdd6 \
raidz2 /dev/disk/by-id/hdd7 ... /dev/disk/by-id/hdd12 \
special mirror /dev/disk/by-id/nvme_meta1 /dev/disk/by-id/nvme_meta2 \
-o ashift=12 -o autoreplace=on
# 文件服务通用数据集
zfs create -o recordsize=1M -o atime=off -o compression=lz4 Pool-FS/share
# 针对小文件的目录可单独定义(覆盖 recordsize 与 primarycache)
zfs create -o recordsize=16K -o primarycache=metadata Pool-FS/smallfiles
动态加挂 SLOG / L2ARC
# SLOG(强烈建议镜像 & 有断电保护的低延迟介质/Optane)
zpool add Pool-DB log mirror /dev/disk/by-id/optane1 /dev/disk/by-id/optane2
zpool add Pool-FS log mirror /dev/disk/by-id/p1600x1 /dev/disk/by-id/p1600x2
# L2ARC(读缓存,容量可大于内存;2.0+ 支持持久)
zpool add Pool-DB cache /dev/disk/by-id/pm9a3_1
zpool add Pool-FS cache /dev/disk/by-id/pm9a3_2
关键点:
- SLOG 只加速“同步写”(sync write),异步写几乎没收益;没有断电保护(PLP)的消费级 NVMe 做 SLOG 风险极高。
- L2ARC 只缓存读;是否有用取决于“热数据是否稳定重复命中、且超出内存 ARC”。
压测方法与脚本片段
1)数据库 OLTP(PostgreSQL)
# 初始化(数据量 > 内存,确保需要二级缓存/L2ARC 才可能有效)
sudo -u postgres pgbench -i -s 20000 # 约 ~320GB 数据集
# 标准 5 分钟压测,128 并发,开启 fsync(默认)
pgbench -c 128 -T 300 -P 10 -M prepared -S # 纯读
pgbench -c 128 -T 300 -P 10 # 读写混合
# ZFS 数据集建议
zfs set recordsize=16K Pool-DB/pgdata
zfs set logbias=latency Pool-DB/pgdata
2)NFS 同步写(模拟 VM/数据库/严格一致性)
# 服务器侧(ZFS)
zfs set sync=always Pool-FS/share # 强制同步写,以暴露 SLOG 的价值
# 客户端侧(fio 小文件同步写)
fio --name=sync-small --directory=/mnt/nfs \
--numjobs=8 --iodepth=1 \
--rw=randwrite --bs=8k --size=20G \
--ioengine=libaio --direct=1 --fsync=1 --runtime=180 --time_based
3)SMB 海量小文件热读
# 先用 rsync 准备 2TB、4~128KB 混合小文件(Zipf 分布)
# 再用 fio/自写脚本做多进程随机读 3~5 分钟,观察 ARC/L2ARC 命中率
测试矩阵
| 方案编号 | 池/介质 | SLOG | L2ARC | 备注 |
|---|---|---|---|---|
| A1 | Pool-DB(SSD 镜像) | 无 | 无 | 基线 |
| A2 | Pool-DB(SSD 镜像) | Optane ×2 镜像 | 无 | 仅 SLOG |
| A3 | Pool-DB(SSD 镜像) | 无 | NVMe 1.92TB | 仅 L2ARC |
| A4 | Pool-DB(SSD 镜像) | Optane ×2 镜像 | NVMe 1.92TB | SLOG+L2ARC |
| F1 | Pool-FS(HDD raidz2+special) | 无 | 无 | 基线 |
| F2 | 同 F1 | Optane/P1600X ×2 镜像 | 无 | 仅 SLOG |
| F3 | 同 F1 | 无 | NVMe 3.84TB | 仅 L2ARC |
| F4 | 同 F1 | Optane/P1600X ×2 镜像 | NVMe 3.84TB | SLOG+L2ARC |
结果一:PostgreSQL OLTP(读写混合)
- 参数:128 并发、300 秒、fsync=on、数据集 ~320GB(超出 ARC)。
- ZFS:recordsize=16K, atime=off, compression=lz4, logbias=latency。
TPS 与延迟(节点 A)
| 方案 | TPS(avg) | p95 延迟(ms) | p99 延迟(ms) | ARC 命中 | L2ARC 命中 |
|---|---|---|---|---|---|
| A1 基线 | 22,480 | 8.7 | 9.8 | 78% | 0% |
| A2 SLOG | 25,920 | 7.2 | 7.9 | 79% | 0% |
| A3 L2ARC | 27,780 | 6.1 | 6.8 | 65% | 27% |
| A4 SLOG+L2ARC | 29,430 | 5.4 | 6.1 | 66% | 29% |
解读:
- SSD 池上 SLOG 仍有收益(~+15% TPS),原因是 OLTP 中存在大量 fsync/同步提交。
- L2ARC 在“稳定热数据”下带来额外提升(~+24%),关键在工作集复用度;“冷热剧烈波动”时收益会收敛。
- SLOG+L2ARC 叠加效果最好,但前提是 SLOG 用低时延且带 PLP 的介质(如 Optane)。
结果二:NFS 强制同步写(小块,8KB,fsync=1)
- 参数:8 任务、iodepth=1、8KB 随机写、sync=always。
- 原因:这类场景最能真实暴露 ZIL/SLOG 的价值(VM、数据库、严苛一致性)。
吞吐与延迟(节点 B)
| 方案 | 吞吐(ops/s) | 平均延迟(ms) | p99 延迟(ms) |
|---|---|---|---|
| F1 基线(无 SLOG) | 1,820 | 41.3 | 82.7 |
| F2 仅 SLOG | 12,060 | 6.2 | 12.4 |
| F3 仅 L2ARC | 1,840 | 40.9 | 82.1 |
| F4 SLOG+L2ARC | 12,140 | 6.1 | 12.1 |
解读:
- HDD 池若无 SLOG,强制同步写几乎“废了”;上了 SLOG 直接 提升 6.6×。
- L2ARC 对写无帮助(它是读缓存)。文件服务若有大量同步写(NFS、某些 SMB/AFP 场景、虚拟化后端),SLOG 是必须品。
结果三:SMB 海量小文件热读(4~128KB)
- 参数:2TB 数据集,小文件占比高,Zipf 分布,25GbE,读 5 分钟取稳态。
- ZFS:Pool-FS 启用 special vdev(元数据/小文件),分别对比是否再加 L2ARC。
读性能(节点 B)
| 方案 | 吞吐(MB/s) | p95 延迟(ms) | p99 延迟(ms) | ARC 命中 | L2ARC 命中 |
|---|---|---|---|---|---|
| F1 基线(有 special,无 L2ARC) | 450 | 31 | 45 | 32% | 0% |
| F3 仅 L2ARC(3.84TB) | 920 | 13 | 18 | 38% | 43% |
| F4 SLOG+L2ARC | 918 | 13 | 18 | 38% | 42% |
解读:
- special vdev 已强烈改善元数据/小文件路径;
- 若热数据远大于内存,再叠加 L2ARC(容量远超内存),读性能继续翻倍;
- SMB 热读几乎不涉及同步写,故 SLOG 基本不影响读性能。
什么时候该上 SLOG?
必须上:
- HDD 为主的池,且 存在同步写(NFS sync=always、VM 存储、数据库日志落盘、Mac SMB 严格一致选项)。
- SSD 池但对 提交延迟极敏感(数据库 commit latency、消息队列)。
谨慎上:
- 业务几乎全是异步写(流式落盘、大文件顺序写),SLOG 价值有限。
选型要点:
- 介质必须有 PLP(断电保护)、低时延、高耐久;Optane 类最佳。
镜像 SLOG 是底线(否则单点风险)。
- SLOG 容量不必很大(常见 16~64GB 足够,视 TXG/并发而定),重在时延与稳定性。
什么时候该上 L2ARC?
收益大:
- 稳定且可复用的热数据,规模>内存,且业务是 读为主(构建/编译缓存、素材库、代码仓库、热媒体片段、只读数据库切片)。
- 已经把内存加到合适规模,仍存在大量重复读取且命中不足。
收益小:
- 热数据剧烈波动、不可复用;或业务主要是写;或已经受 网络瓶颈 限制。
选型要点:
- L2ARC 容量可以 远大于内存;OpenZFS 2.0+ 支持 持久化 L2ARC,重启后不需要长时间预热。
- special vdev + L2ARC 协同:special vdev 解决“寻道/元数据”,L2ARC 解决“大量热读”——二者叠加在文件服务上体验极好。
数据库与文件服务的选型建议(香港机房实战版)
数据库(PostgreSQL/MySQL,OLTP 为主)
磁盘布局:镜像 vdev(RAID10 类)优先于 raidz,追求低时延与并发。
SLOG:上(镜像),尤其对 fsync/同步提交敏感的业务。
L2ARC:可选(看热数据复用度与预算,优先加内存)。
参数建议:
- recordsize=16K(与数据库页更贴近),atime=off,compression=lz4,logbias=latency。
- HBA IT 模式直通,ashift=12,autotrim=on(SSD)。
- PostgreSQL 侧 effective_io_concurrency、shared_buffers 与检查点参数需配合调优。
推荐最小配置(我们这次用的稳定档):
- 6 × 企业级 SSD 组成 3 组镜像 vdev;
- 2 × Optane SLOG 镜像;
- 内存 ≥ 128GB;
- L2ARC 1~2TB 视预算/命中率加。
文件服务(SMB/NFS,混合大小文件)
磁盘布局:大容量优先可选 raidz2;若小文件/并发元数据压力大,加 special vdev(镜像 NVMe)。
SLOG:
- NFS(sync=always)/虚拟化后端 → 必须上;
- 纯 SMB 非严格同步写 → 可不必。
L2ARC:
- 热数据远大于内存、且复用度高 → 强烈建议(容量 2~4TB 起步)。
参数建议:
- 大文件共享:recordsize=1M;小文件目录:recordsize=16K、primarycache=metadata。
- atime=off,compression=lz4;special vdev 镜像、启用 autotrim(NVMe)。
关键调优片段(仅列要点,生产需结合实况评估)
# 减少 ARC 抖动(示例值,按内存调)
echo 107374182400 > /sys/module/zfs/parameters/zfs_arc_max # ~100GB
echo 85899345920 > /sys/module/zfs/parameters/zfs_arc_min # ~80GB
# L2ARC 预取与写速率(按介质与业务微调)
echo 1 > /sys/module/zfs/parameters/l2arc_noprefetch
echo 64 > /sys/module/zfs/parameters/l2arc_write_max_mb
# ZIL 提交节奏(谨慎调整)
echo 5 > /sys/module/zfs/parameters/zfs_txg_timeout
踩坑与现场解决
SLOG 用了“看上去很快”的消费 NVMe:
- 无 PLP,断电后 数据风险;而且写放大严重,寿命掉得飞快。
- 解决:换 Optane / 企业盘,并做镜像。
忘了设置 ashift=12:
- HDD/SSD 4K 物理扇区在 ashift=9 下会引发对齐问题,随机写性能雪崩。重建后性能恢复。
L2ARC 太小且热数据剧烈波动:
- 频繁替换导致命中率不升反降,还占用内存做元数据。
- 解决:加大 L2ARC 容量(≥ 2TB),并限制 l2arc_write_max_mb。
special vdev 没做镜像:
- 元数据丢失=池子瘫痪的概率陡增。
- 解决:special vdev 必须镜像。
以为 SLOG 能提速异步写:
- 事实并非如此;异步写依然受到底层盘与 TXG 刷新的约束。
NFS/SMB 没开 Jumbo Frame:
- CPU 抬升、延迟恶化。
- 解决:交换机/主机两端统一 MTU=9000,并验证路径无碎片。
成本 vs. 性能的现实平衡(香港场景)
香港机房带宽/机柜成本高,但网络路径好、时延稳;选对少量关键部件(Optane SLOG、合适容量 L2ARC、special vdev),往往比“堆很多通用盘”更划算。
我的经验是:数据库优先“低时延 + 稳定提交”(镜像 vdev + SLOG);文件服务优先“元数据与热读”(special vdev + L2ARC)。两类业务分池,互不拖累,是 TCO 最优解。
- 数据库 OLTP:Mirror vdev + SLOG(镜像/Optane) 必上;L2ARC 视热数据而定(先把内存加够)。
- 文件服务:raidz2 + special vdev(镜像 NVMe) 起步;NFS 有同步写就上 SLOG;稳定热读就上 L2ARC。
- 千万别用消费级无 PLP 的 NVMe 做 SLOG;special vdev 和 SLOG 都要镜像。
把最后一轮 pgbench 停下来的时候,数据库的 p99 延迟稳定到 6ms 左右;NFS 同步写吞吐恢复到了 10GbE 的线速,SMB 热读也能稳稳压在 900MB/s 附近。客户那边的工单也终于安静了。我把两台机器的风扇档位调回自动,灯一灭,机房的风好像也温柔了些。
那一刻我知道——不是 ZFS 多神,而是我们在对的场景里,给了它对的刀。
附录:常用命令清单(可直接套用)
# 查看池状态与写日志/缓存
zpool status
zpool iostat -v 1
arcstat.py 1
arc_summary.py
# 数据集属性回顾
zfs get recordsize,compression,atime,logbias,primarycache Pool-DB/pgdata
zfs get recordsize,primarycache Pool-FS/smallfiles
# 安全移除/更换设备
zpool offline Pool-DB /dev/disk/by-id/ssd3
zpool replace Pool-DB /dev/disk/by-id/ssd3 /dev/disk/by-id/ssd3_new
zpool remove Pool-DB /dev/disk/by-id/pm9a3_1 # 移除 L2ARC(不影响数据)
如果你的场景和我上述两个不完全一致,按这篇文章的思路,把业务 I/O 模型先分类,再决定 SLOG / L2ARC / special vdev 的取舍和大小,就能少走很多弯路。