1. 真实场景:电商数据库IO告警
2024年双十一,我们的MySQL主库(8节点RAC)突然报警:AWR报告显示DB File Sequential Read平均等待6.7ms,Log File Sync居然到了120ms。TPS从峰值2.3万掉到0.8万,前端页面开始报504。这台服务器配置:2颗Intel Xeon 6338(28核)、512GB DDR4-2933、LSI 9361-8i RAID卡(缓存2GB)、12块Samsung PM1643a 2.4TB SAS SSD(RAID10)、Mellanox ConnectX-4 Lx 25GbE网卡。RAID卡BBU已过期,被迫用Write Through模式,写入性能骤降。实测fio随机写IOPS只有8万,延迟超过5ms。网络端,25GbE实测吞吐只有18Gbps,原因是网卡IRQ绑定不均,再加上RX/TX ring buffer默认只有512,丢包率0.3%。
根本原因:RAID卡成了IO栈的瓶颈。SAS SSD自身延迟已经到0.3ms,但RAID卡协议转换、缓存算法、PCIe 3.0 x8带宽(8GB/s)进一步拉高延迟。同时25GbE网卡在小包场景下CPU占用率高,内存带宽瓶颈(单通道DDR4理论51.2GB/s,多个进程争抢)。我们决定换方案:去掉RAID卡,用CPU直通NVMe U.2 SSD,网卡升级到Mellanox ConnectX-5 100GbE,并启用RDMA。
2. 方案对比:传统RAID10 vs NVMe直通+高速网络
2.1 方案A:RAID10 + SAS SSD + 25GbE(改造前)
- 存储:12块SAS SSD,LSI 9361-8i RAID卡,RAID10,总容量14.4TB(可用的7.2TB)。缓存设置Write Through,BBU失效导致不能Write Back。
- 网络:Mellanox ConnectX-4 Lx 25GbE,双口,MTU 1500,未开启RSS。
- 成本:SSD X12 + RAID卡 + 网卡 ≈ 12 * 8000 + 3500 + 2000 = 101,500元(单位人民币,2019年价格)
2.2 方案B:NVMe直通 + 100GbE(改造后)
- 存储:8块Samsung PM9A3 3.84TB NVMe U.2 SSD,通过CPU直连(无RAID卡,软RAID1做系统盘,数据盘用zfs pool or lvm条带化)。PCIe 4.0 x4每块,单向7.5GB/s。
- 网络:Mellanox ConnectX-5 100GbE(HDR100),单口,开启RDMA(RoCE v2)。
- 成本:SSD X8 + 网卡 ≈ 8 * 12000 + 12000 = 108,000元。没有RAID卡节省3500,贵不到7%。
2.3 关键差异
| 特性 | 方案A(传统RAID10) | 方案B(NVMe直通) |
|---|---|---|
| 存储接口 | SAS 12Gb/s(单链路) | PCIe 4.0 x4(7.5GB/s) |
| 协议栈 | SCSI -> RAID卡 -> PCIe | NVMe -> 直连PCIe |
| 最大随机读IOPS(单盘) | ~700K(RAID卡限制) | ~1.5M(CPU直通) |
| 端到端延迟(写) | 0.5~2ms(Write Through) | 0.1~0.3ms(同步写) |
| 网络带宽 | 25GbE(理论3.125GB/s,实际<2GB/s) | 100GbE(理论12.5GB/s,实际~10GB/s) |
| 网络延迟(小包) | ~50us(TCP) | ~5us(RoCE) |
3. 方案实现与压测代码
3.1 准备环境
OS: CentOS 7.9 (内核3.10.0-1160),后续升级到5.4.262以获得更好的NVMe驱动。fio版本3.35。MySQL 8.0.35。网卡驱动:MLNX_OFED 5.8-1.0.1.1。
3.2 方案A压测(RAID10 + SAS SSD)
直接挂载RAID10为/dev/md0。fio脚本:
# fio_raid10.ini
[global]
ioengine=libaio
direct=1
bs=4k
iodepth=64
runtime=120
group_reporting
randrepeat=0
[randread-4k]
rw=randread
filename=/dev/md0
size=100G
numjobs=16
[randwrite-4k]
rw=randwrite
filename=/dev/md0
size=100G
numjobs=16
执行:fio fio_raid10.ini。结果:随机读IOPS 82k,延迟95us;随机写IOPS 79k,延迟5.8ms(因为Write Through)。
3.3 方案B压测(NVMe直通)
NVMe设备一般是/dev/nvme0n1、/dev/nvme1n1等。注意:不要在操作系统层面做md RAID,我们使用LVM条带化(striped LV)或者ZFS zpool。这里用LVM示范:
# 创建pv/vg/lv,条带化8块NVMe
pvcreate /dev/nvme0n1 /dev/nvme1n1 ... /dev/nvme7n1
vgcreate vg_nvme /dev/nvme0n1 /dev/nvme1n1 ... /dev/nvme7n1
lvcreate -i8 -I4k -L7TB -n lv_data vg_nvme
# 格式化xfs(注意sunit/swidth)
mkfs.xfs -f -d su=4k,sw=8 /dev/vg_nvme/lv_data
mount -o noatime,nodiratime,nobarrier /dev/vg_nvme/lv_data /data
# fio压测脚本
[global]
ioengine=libaio
direct=1
bs=4k
iodepth=64
runtime=120
group_reporting
randrepeat=0
[randread-nvme]
rw=randread
filename=/data/testfile
size=200G
numjobs=32
[randwrite-nvme]
rw=randwrite
filename=/data/testfile
size=200G
numjobs=32
结果:随机读IOPS 352k,延迟22us;随机写IOPS 298k,延迟35us。
3.4 MySQL配置调优
针对NVMe低延迟特性,我们调整了:
-- MySQL 8.0.35 参数变更
SET GLOBAL innodb_flush_log_at_trx_commit = 1; -- 默认,但NVMe下可承受
SET GLOBAL innodb_log_file_size = 2G; -- 增大减少fsync频率
SET GLOBAL innodb_log_write_ahead_size = 8192;
SET GLOBAL innodb_use_native_aio = ON;
SET GLOBAL innodb_thread_concurrency = 64;
SET GLOBAL innodb_io_capacity = 40000;
SET GLOBAL innodb_io_capacity_max = 100000;
-- 关闭双写缓冲(NVMe原子写安全)
SET GLOBAL innodb_doublewrite = OFF; -- 仅在NVMe+BBU且不担心页损坏时
注意:innodb_doublewrite = OFF需要确认存储支持原子写(NVMe原生支持)。如果担心,可以保留。
3.5 网络调优命令
针对Mellanox ConnectX-5 100GbE,我们启用RDMA并调整ring buffer:
# 设置MTU 9000(jumbo frames)
ip link set dev ens1f0 mtu 9000
# 调整RX/TX ring buffer (当前默认512)
ethtool -G ens1f0 rx 8192 tx 8192
# 开启RSS (Receive Side Scaling)
ethtool -X ens1f0 weight 1 1 1 1
# 队列中断分配 (避免中断冲突)
set_irq_affinity.sh ens1f0
# 启用RoCE (RDMA over Converged Ethernet)
# 确保交换机PFC配置好
echo 0 > /sys/class/net/ens1f0/device/roce_enable
# 使用iperf3测试带宽
iperf3 -c 10.0.0.2 -t 60 -P 16 --bidir
通过iperf3,方案A(25GbE)只能达到18Gbps,方案B(100GbE)达到98Gbps(线速98%)。
3.6 综合压测数据
| 指标 | 方案A (RAID10+SAS SSD+25GbE) | 方案B (NVMe直通+100GbE) |
|---|---|---|
| 4K随机读IOPS | 82,000 | 352,000 |
| 4K随机写IOPS | 79,000 | 298,000 |
| 4K随机读延迟(μs) | 95 | 22 |
| 4K随机写延迟(μs) | 5800 | 35 |
| 128K顺序读吞吐(MB/s) | 2,300 | 7,200 |
| 128K顺序写吞吐(MB/s) | 1,800 | 6,800 |
| MySQL TPS(sysbench OLTP_RW) | 8,200 | 35,500 |
| 网络吞吐(iperf3单流) | 18 Gb/s | 98 Gb/s |
| 网络延迟(interrupt模式) | 50 μs | 5 μs (RoCE) |
| 总成本(元) | 101,500 | 108,000 |
4. 避坑指南
这里列出我实际踩过的4个坑,你大概率也会遇到。
- 坑1:RAID卡缓存Write Through不如NVMe直接写。我们当时因为BBU过期只能Write Through,写入性能惨不忍睹。即使买新BBU,Write Back模式下RAID卡缓存也就2GB,写密集场景缓存打满后降速。NVMe直通没有这个中间层,延迟稳定。所以如果你要写密集型,直接上NVMe。
- 坑2:NVMe热插拔引发panic。有一块NVMe盘报SMART警告,我们没优雅摘盘直接拔了,系统立刻kernel panic(因为nVMe驱动在4.19之前有bug)。后来必须先用
nvme detach-ns和echo 1 > /sys/block/nvme0n1/device/remove,再物理拔出。升级内核到5.4+解决了热插拔问题。 - 坑3:100GbE网卡双工协商问题。新换ConnectX-5后,用25GbE旧交换机连接,网卡自动协商到100GbE Full但实际链路质量差,丢包严重。后来发现必须用QSFP28光模块和对应交换机,端口配置speed 100g。最好两台机器直连(back-to-back)测试排除交换机问题。
- 坑4:MySQL双写缓冲关掉后数据损坏。我们在压测时并发insert,突然掉电测试(拔电源),重启后有一行数据page0校验错误。因为NVMe虽然原子写但断电时仍可能有部分写入。所以生产环境不建议关掉innodb_doublewrite,除非你BBU+RAID卡或企业级NVMe+掉电保护电容。我们后来花了2天恢复备份。双重保险。
5. 总结
如果你的数据库IOPS已经超过5万,或者延迟要求在1ms以下,NSAID方案(RAID10+SAS SSD)已经过时。直接上NVMe直通,去掉RAID卡,配合100GbE网络(至少25GbE),能获得4倍IOPS和10倍延迟改善。成本涨幅不到7%,但性能提升是划时代的。别忘了调优驱动和内核参数,以及避开上面4个坑。
后面我会写一篇NVMe over Fabrics的分布式存储设计,把网络延迟降到2μs。