diff --git "a/linux/Linux IO \346\200\247\350\203\275\351\227\256\351\242\230\344\270\216\350\247\243\345\206\263\346\200\235\350\267\257.md" "b/linux/Linux IO \346\200\247\350\203\275\351\227\256\351\242\230\344\270\216\350\247\243\345\206\263\346\200\235\350\267\257.md" new file mode 100644 index 00000000..e9c17db3 --- /dev/null +++ "b/linux/Linux IO \346\200\247\350\203\275\351\227\256\351\242\230\344\270\216\350\247\243\345\206\263\346\200\235\350\267\257.md" @@ -0,0 +1,80 @@ +# Linux SSD IO 性能问题与解决思路 + + +## 一、磁盘 IOPS 跟读写速率都很低,但 IO 利用率一直 100%,应用程序响应很慢 + +### 1 案例一:AIGC 程序 + +环境: + +1. 系统:Amazon Linux 2 +2. 磁盘:AWS GP3 类型,IOPS 3000,吞吐量 750MB/s +3. 应用程序:AIGC 推理服务 + +现象: + +node-exporter 提供的磁盘相关监控指标: + +![](./_img/node-exporter-high-io-wait.webp) + +AIGC 推理程序在首次运行时会因为 IO 问题卡 15 分钟,后续运行时因为数据已经缓存到内存中,所以不会再出现这种卡顿。 + +把 GP3 的吞吐上限改到 750MB/s,没任何改善。 +鉴于监控中的 IOPS 也不超过 100,预计把 IOPS 调高也没啥用。 + + +### 2 案例二:Kafka 节点 + +线上环境中经常遇到某 Kafka 节点因为磁盘 IO 跑满导致响应满,无法正常提供服务。 +但监控看 IOPS 跟读写速率都很低,只有 IO 使用率一直 100%。 + + +### 3 排查思路 + +#### 3.1. 底层原理分析 + +要搞明白这个问题的原因,需要首先理解 HDD/SSD 的性能区别,以及 Linux 系统中相应的驱动程序架构,如下这篇文章对此有比较详细的说明: + +- [【阿里云总监课】存储系统设计——NVMe SSD性能影响因素一探究竟](https://developer.aliyun.com/article/658502) + +简单的说,HDD 是通过单个磁头读写数据,所以针对 HDD 的 AHCI 规范只定义了一个命令队列,即不论 CPU 有几个核,HDD 都只能同时处理一个 IO 请求。 + +而 SSD 的性能全方位地超越了 HDD,并且硬件层面就支持多个并发 IO,所以针对 SSD 的 NVMe 规范采用了多队列设计。从软件层面看,每个 CPU Core 都可以创建一对 Queue Pair 和 SSD 进行数据交互。 + +SSD 的性能受两方面的影响: + +1. 硬件层面:比如 NAND Flash 的类别(TLC/QLC/SLC/MLC/...)、后端通道数(CE数量)及总线频率、SSD 控制器的处理能力与架构、PCIe 带宽、使用寿命等等。 + 1. 由于我们这里主要考虑云服务场景,硬件层面的因素不展开讨论。 +2. 软件层面:即 Linux 系统层面 + 1. **数据布局方式**:数据布局方法需要充分考虑NAND Flash中的并发单元,如何将IO操作转换成NAND Flash的并发操作,这是数据布局需要考虑的问题。例如,采用数据交错的方式在多通道page上进行数据布局,通过这种方式可以优化顺序带宽。 + 2. **垃圾回收 / wear leveling 调度方法**:数据回收、wear leveling、data retention等操作会产生大量的NAND Flash后端流量,后端流量直接反应了SSD的写放大系数,也直接体现在后端带宽的占用。 + 3. **Overprovisioning 预留空间**:为了解决坏块、垃圾回收等问题,在SSD内部预留了一部分空闲资源作为备用,即 Overprovisioning 空间。 + 4. **IO调度算法**:NAND Flash具有严重的性能不对称性,Flash Erase和Program具有ms级延迟,Flash read的延迟在us级。因此,如何调度Erase、Program以及read是SSD后端设计需要考虑的问题。另外,前端IO以及背景IO之间的调度也是需要权衡考虑,通过IO调度可以达到最佳性能表现。在IO调度过程中,还需要利用NAND Flash的特性,例如Program Suspension,通过这些特性的利用,最优化SSD前端IO性能。 + 5. **IO Pattern**:IO Pattern影响了SSD内部的GC数据布局,间接影响了GC过程中的数据搬移量,决定了后端流量。当IO Pattern为全顺序时,这种Pattern对SSD内部GC是最为友好的,写放大接近于1,因此具有最好的性能;当IO Pattern为小块随机时,会产生较多的GC搬移数据量,因此性能大为下降。在实际应用中,需要通过本地文件系统最优化IO Pattern,获取最佳性能。 + 6. FTL算法 / Bit error 处理机制,这几个不展开了。 + 7. 系统层面的系统优化:比如内存中的 Page Cache、文件系统、SSD 驱动程序的实现方式等等。 + +我们在前面看到的两个案例中,都是因为 IO 利用率一直 100%,但 IOPS 和读写速率都很低。GP3 是 AWS 目前的主流 EBS 磁盘类型,在软件层面肯定能做一些优化。 + + +#### 3.2. 排查 + +首先我们已经通过 node-exporter 对应的 grafana 监控面板,确定了当前磁盘 IO 利用率一直 100%、IOPS 与读写速率都很低,一开始读的 wait 显然非常高,能确认是卡在了读上面。 + +进一步可用 `iotop` 定位到消耗 IO 的进程,然后用 `strace -f -p $pid -T -tt -e read` 与 `lsof -p $pid` 定位到导致 IO 高的文件。 + +#### 4. 解决思路 + +常见手段: + +1. 针对磁盘和应用程序 I/O 模式的特征,我们可以选择最适合的 I/O 调度算法。云主机一般都默认用 `noop` 算法,但对于 kafka / mysql / aigc 等应用场景,`deadline` 可能更合适。 + 1. If in doubt, use the `deadline` scheduler. +2. 对于顺序读较多的场景,调整 /sys/block/sdb/queue/read_ahead_kb 的值,增大磁盘的预读数据。 +3. 对应用程序进行磁盘级别的隔离,比如把 kafka 的数据目录和日志目录这些 IO 压力较高的目录分别放到不同的磁盘上。 +4. 应用程序层面的优化,比如 kafka 自身就通过追加写的方式来减少磁盘随机写的次数,从而提高性能。kafka 本身也有一些 IO 行为相关的参数可调整。 +5. 对于云主机,可以考虑使用本地存储卷(Local SSD / Local NVMe Storage),它的性能远高于 AWS EBS / Aliyun ESSD 等云磁盘,但其中的数据在多种情况下并不持久化,需要自己做好数据备份与容灾考量。 + +## 参考 + +- [Linux性能优化实践-磁盘I/O篇](https://mp.weixin.qq.com/s/mLEVKXShnifHbQiQrgJGSA) + diff --git a/linux/_img/node-exporter-high-io-wait.webp b/linux/_img/node-exporter-high-io-wait.webp new file mode 100644 index 00000000..b0c0b819 Binary files /dev/null and b/linux/_img/node-exporter-high-io-wait.webp differ