一台跑 MySQL 的服务器,硬件升级的顺序通常是:先换 SSD,再考虑加 CPU。原因很简单——数据库绝大部分的性能瓶颈最终会落到"一次随机读要多久、一次 fsync 要多久"上。但采购时面对的参数表很迷惑:同样是 3.84TB,价格可能相差两三倍;标称的"随机写 500000 IOPS"往往是在队列深度 128、全盘 SLC 缓存状态下测出来的。本文从数据库的真实 I/O 特征出发,讲清楚该看哪些指标、怎么用 fio 自己测、以及在容量和寿命上怎么留余量。
一、数据库的 I/O 画像:什么样的盘才是好盘
弄清楚数据库怎么读写磁盘,才知道该看厂商参数的哪一项:
- InnoDB 数据页:
innodb_page_size默认 16KB,缓冲池未命中时做 16KB 的随机读;后台线程做 16KB 的随机写刷脏。所以数据库的标志性负载是"小块随机读写",而不是顺序读写。 - redo log:顺序写,但每次事务提交都可能触发 fsync。
innodb_flush_log_at_trx_commit=1时每个事务一次 fsync,加上sync_binlog=1就是两次 fsync。单次 fsync 的延迟直接决定单实例 TPS 上限:如果 fsync 要 2 毫秒,理论上限约 500 TPS/commit。 - binlog:追加顺序写,对带宽敏感。
- 临时表、排序、doublewrite buffer:写入量大且不规则,也会占 IO。
- 读不外溢时的 bp 命中率>99% 意味着大部分请求不碰盘,所以一块好盘主要是在"少数重要时刻"救命。
| 指标 | 为什么重要 | 参考标准(数据库场景) |
|---|---|---|
| 4KB 随机读 IOPS | 决定索引 Lookup 类 OLTP 的吞吐 | 企业级 NVMe 通常数十万到百万级;SATA SSD 约 7 万-10 万 |
| QD1 延迟 | 低并发下真实体验(数据库常常排队不多) | 读 < 100 微秒;写(含 fsync)看 PLP |
| 单次 fsync 延迟 | 决定 commit TPS | 带 PLP 的盘通常 < 200 微秒;无 PLP 消费级盘可能 1-10 毫秒 |
| 顺序写带宽 | 影响备份、大批量导入、binlog 写入 | PCIe 4.0 x4 约 2-6 GB/s;SATA III 上限约 550 MB/s |
| DWPD / TBW | 寿命 | 数据库建议 DWPD ≥ 1,至少 0.8 |
| 掉电保护 PLP | DRAM 缓存里的脏数据能否安全落盘 | 数据库必须有,否则 fsync 语义不可靠 |
| QoS / P99 延迟 | 稳态一致性 | 稳态 P99 抖动小比峰值 IOPS 更重要 |
这里必须强调掉电保护(Power Loss Protection,PLP)。企业级 SSD 一般带钽电容或超级电容,断电时把 DRAM 缓存里的数据刷到 NAND,因此磁盘可以"假装"已经完成 fsync 而不必真的写入 NAND,延迟极低且保证持久化。消费级 SSD 没有 PLP,为了保证持久化只能老实完成 NAND 写入,fsync 延迟可能高出几十倍——这就是为什么"跑分很高但数据库很慢"的现象经常出现在用了消费级盘的机器上。
二、接口与形态:别在接口上先输了
| 形态 | 接口带宽 | 典型随机读 IOPS | 适用场景 |
|---|---|---|---|
| SATA SSD | 6 Gbps,实际约 550 MB/s | 7 万-10 万 | 数据量小、预算敏感的从库或测试环境 |
| SAS SSD(12G / 24G) | 12-24 Gbps | 10 万-20 万 | 已有 SAS 背板、要求双端口高可用的环境 |
| NVMe U.2 / U.3 | PCIe 3.0/4.0 x4 | 40 万-100 万+ | 主流数据库服务器首选,支持热插拔 |
| NVMe AIC(插卡式) | PCIe 4.0/5.0 x4 或 x8 | 百万级以上 | 本地盘高吞吐需求,占用 PCIe 插槽 |
| 云硬盘(ESSD 等) | 规格由厂商定义 | 随规格与容量线性提升 | 弹性好;IOPS 通常和容量挂钩 |
云数据库场景下有一个常见陷阱:ESSD 类云盘的 IOPS 上限往往随容量增长(例如每 GB 提供若干 IOPS),所以"买了一块 100GB 的高性能盘"很可能 IOPS 上限低于预期。采购时应先看目标 IOPS,再反推容量,而不是相反。
三、寿命评估:DWPD 与 TBW 怎么算
两个概念:DWPD(Drive Writes Per Day)指在质保期内每天可以全盘写入几次;TBW(Terabytes Written)指生命周期内累计允许写入的总字节数。二者可以互相推算:
TBW ≈ DWPD × 盘容量(TB) × 365 × 质保年限 示例:3.84TB、DWPD = 1、质保 5 年 TBW = 1 × 3.84 × 365 × 5 = 7008 TBW 每天允许写入量 = 1 × 3.84TB = 3.84 TB/天 折合平均写入带宽 = 3.84TB / 86400s ≈ 44.4 MB/s(持续 5 年不中断)
接着把自己数据库的实际写入量算出来做比对:
# MySQL 实际写入数据量估算:取两次 Innodb_data_written 与 Innodb_os_log_written 的差值
mysql -uroot -p -e "SHOW GLOBAL STATUS LIKE 'Innodb_data_written';
SHOW GLOBAL STATUS LIKE 'Innodb_os_log_written';
SHOW GLOBAL STATUS LIKE 'Innodb_pages_written';
SHOW GLOBAL STATUS LIKE 'Uptime';"
# 用差值 ÷ 时间得到每秒写字节数,再乘以 86400 得到每天写入量
# 注意还要算上:binlog 写入、临时表、备份/数据导入产生的额外写放大
举例:一台 MySQL 每天实际写入 200GB,用 3.84TB / DWPD=1 的盘,每天配额 3.84TB,余量非常充足;但如果每天写入 800GB 且还有频繁的后台刷脏(写放大 2-3 倍),就应该考虑 DWPD=3 的写密集型盘。经验判断阈值:
- 日均写入 < 盘容量 × 0.3:读密集型(DWPD 0.5-1)足够。
- 日均写入 < 盘容量 × 1:混合型 DWPD=1。
- 日均写入接近或超过盘容量:务必 DWPD ≥ 3,并加 OP。
- 消耗到自身的 50%-70% TW 就应该规划更换,而不是等到 100%。
# 查看 NVMe 健康度(Percentage Used 即寿命消耗百分比) nvme smart-log /dev/nvme0 smartctl -a /dev/nvme0 | grep -iE 'Percentage Used|Available Spare|Media and Data Integrity' # SATA SSD smartctl -a /dev/sda | grep -iE 'Percentage Used|Wear_Leveling|Total_LBAs_Written'
四、用 fio 自己测,而不是看宣传页
下面这套命令覆盖了数据库最关心的三种模式,建议在新机器上线前跑一遍并留档。
# 1) 16KB 随机读(模拟 InnoDB 缓冲池未命中的索引/数据页读取)
fio --name=db_randr --filename=/data/fio.test --size=20G \
--ioengine=libaio --direct=1 --rw=randread --bs=16k \
--numjobs=4 --iodepth=32 --runtime=120 --time_based --group_reporting
# 2) 16KB 随机写(模拟后台刷脏)
fio --name=db_randw --filename=/data/fio.test --size=20G \
--ioengine=libaio --direct=1 --rw=randwrite --bs=16k \
--numjobs=4 --iodepth=32 --runtime=120 --time_based --group_reporting
# 3) 同步写 + fsync(模拟 redo log / binlog 的持久化开销,最关键的一项)
fio --name=db_fs --filename=/data/fio.test --size=20G \
--ioengine=libaio --direct=1 --rw=randwrite --bs=4k \
--fsync=1 --numjobs=1 --iodepth=1 --runtime=60 --time_based --group_reporting
# 4) 70% 读 + 30% 写的混合负载(更接近真实 OLTP)
fio --name=db_mix --filename=/data/fio.test --size=20G \
--ioengine=libaio --direct=1 --rw=randrw --rwmixread=70 --bs=8k \
--numjobs=4 --iodepth=16 --runtime=180 --time_based --group_reporting
怎么读结果:看 IOPS 更要看延迟分布。输出的 lat 段里会有 slat(提交延迟)、clat(完成延迟)、lat(总延迟)以及 percentiles 列表。重点关注:
- 第 3 项(fsync=1、iodepth=1)的 lat 平均值:这是 commit 的延迟下限,大于 1 毫秒说明盘不适合跑高强度事务。
- 99.00 / 99.90 百分位延迟:如果 P99 是平均值的 5 倍以上,说明存在严重抖动。
- 测试前务必做稳态预处理:新的空盘 SLC 缓存会把写入吸收掉,跑完 2 分钟看到的是"作弊"的成绩。建议先随机写满全盘再测。
- 不要用
dd测性能,dd默认是带缓冲的顺序写,和数据库的负载画像毫无关系;如果一定要用,至少加oflag=direct。
# 消除缓存影响的顺序写参考(仍然不如 fio 准确) dd if=/dev/zero of=/data/test bs=1M count=4096 oflag=direct
五、容量怎么留余量
数据库服务器的磁盘容量不是"数据多大就买多大":
所需磁盘容量 ≈ (数据 + 索引) × 1.3 增长预留
+ binlog 占用(受 binlog_expire_logs_seconds 控制,建议预留几十到几百 GB)
+ 临时文件 / tmpdir / 排序溢出(ibtmp1 可配 innodb_temp_data_file_path 限制上限)
+ redo log(MySQL 8.0.30 起由 innodb_redo_log_capacity 控制,默认 100MB)
+ 一次全物理备份的落地空间(若在本机备份)
+ SSD 自身 OP(企业级通常已预留 7% 或 28%)
除了总量,还有几条硬约束:
- 使用率不要超过 70%-80%:SSD 满了之后写放大急剧上升,延迟会明显劣化,寿命也会加速消耗。
- ibd 文件不会自动收缩:DELETE 之后空间并不归还给操作系统,要真正回收需要
OPTIMIZE TABLE,而它需要额外一整张表大小的空闲空间。这就是突发告警的常见原因——磁盘看着还剩 30%,optimize 一张大表直接撑满。 - binlog 要有独立的清理策略,不要让它在磁盘上无限增长。
六、配套配置:买了好盘还要用好
# MySQL 参数(SSD 场景必改) [mysqld] innodb_io_capacity = 2000 # 默认 200 是为机械盘设计的,SSD 应大幅提高 innodb_io_capacity_max = 4000 innodb_flush_neighbors = 0 # SSD 相邻页合并刷没意义,默认 MySQL 8.0 起已是 0 innodb_flush_method = O_DIRECT # 绕过 OS page cache,避免双缓冲 innodb_flush_log_at_trx_commit = 1 # 强一致;从库可放宽到 2 sync_binlog = 1 # 文件系统挂载建议 /dev/nvme0n1 /data xfs defaults,noatime,nodiratime 0 0
# I/O 调度器:NVMe 建议 none,SATA SSD 建议 deadline/kyber,机械盘用 mq-deadline 或 bfq cat /sys/block/nvme0n1/queue/scheduler echo none > /sys/block/nvme0n1/queue/scheduler echo deadline > /sys/block/sda/queue/scheduler # 关闭 Transparent Huge Pages(Redis / MongoDB 官方明确建议关闭) echo never > /sys/kernel/mm/transparent_hugepage/enabled echo never > /sys/kernel/mm/transparent_hugepage/defrag # 预读/read_ahead_kb 对随机读为主的数据库可以调小 cat /sys/block/nvme0n1/queue/read_ahead_kb
关于 RAID:数据库场景推荐 RAID 10(读写性能与冗余兼顾),RAID 5 / 6 因为有"读-改-写"惩罚,随机写性能可能只有单盘的 1/4 左右。若使用带缓存的 RAID 卡,必须先确认有 BBU / 超级电容,否则 Write-Back 策略在断电时会丢数据,退化为 Write-Through 后性能又会大幅下降。直接上 NVMe 并用软件管理时,两盘做 RAID1 也是一种务实选择。
七、选型与上线检查清单
- 先算清楚当前与未来 12 个月的数据总量,确定容量而非先选型号。
- 估算日均写入量(Innodb_data_written + Innodb_os_log_written + binlog),对照 DWPD 选型。
- 确认必须带掉电保护(PLP),并要求厂商在规格书中明确。
- 选择 NVMe U.2 优先;SATA SSD 只用于小数据量或非核心从库。
- 到货后用 fio 做四项测试(16K 随机读、16K 随机写、4K 同步 fsync、混合负载),与采购规格对比留档。
- 测试前做稳态预处理(随机写满全盘),避免 SLC 缓存带来的假高分。
- 核对 MySQL 的 innodb_io_capacity、innodb_flush_neighbors、innodb_flush_method。
- 调整 I/O 调度器、关闭 THP、核对挂载参数。
- 设定磁盘使用率告警阈值(建议 75% 预警、85% 告警)。
- 把设备的 Percentage Used 与不可纠正错误计数纳入月度巡检。
小结
给数据库选 SSD,优先级依次是:持久化的可靠性(PLP)> 单次 fsync 延迟 > 稳态随机读写 IOPS > 带宽 > 容量。容量上要同时预留数据增长、binlog、临时文件、备份和 OPTIMIZE TABLE 所需的空间,日常使用率建议控制在 70% 以内。买了好盘之后还要配上正确的 MySQL 参数与内核 I/O 调度策略,否则昂贵的 NVMe 可能只跑出机械盘几倍的效果。避坑清单:不要用消费级 SSD 跑有强一致要求的主库;不要只看标称 IOPS 不看 QD1 与 P99 延迟;不要让磁盘无限增长到 90% 以上;不要把不同寿命、不同型号的盘混在同一实例里做 RAID。
企业QQ咨询




