什么等等? I/O Wait ≠ I/O 瓶颈?

本文探讨了I/OWait指标的误解,通过实验展示了即使I/OWait值低,磁盘I/O仍可能成为瓶颈。作者使用sysbench、vmstat和内核代码分析,强调了判断I/O瓶颈需综合考虑多个因素。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文地址:什么等等? I/O Wait ≠ I/O 瓶颈? | 深入浅出 eBPF

1. I/O Wait 定义

I/O Wait 是针对单个 CPU 的性能指标,表示当 CPU 分发队列(在睡眠态)里有线程被阻塞在磁盘 I/O 上时消耗的空闲时间。CPU 的空闲时间划分成无所事事的真正空闲时间和阻塞在磁盘 I/O 上的时间。较高的 CPU I/O Wait 时间表示磁盘可能存在瓶颈,导致 CPU 等待而空闲。如果你看到这个定义,有点雾中看花的感觉,那么请移步往后,相信你看完本文的测试验证过程,再回来读上述定义的时候一定会有不同的认识。

2. 测试验证

本地测试验证环境为:Ubuntu 22.04, CPU 12 核,内核 6.2.0-34-generic

$ cat /proc/cpuinfo |grep processor|wc -l
12

我们通过 sysbench 工具进行对应压测验证,同时观察对应的 I/O Wait 的表现。注意为了更好体现效果,我们测试 I/O 的过程中,将 threads 设置为 CPU 的全部核数的 50%(本文为 --threads=6

$ sudo apt-get update 
$ sudo apt install sysbench

# 提前准备测试的数据
$ sysbench  --threads=6 --time=0 --max-requests=0  fileio --file-num=1 \
	--file-total-size=10G --file-io-mode=sync --file-extra-flags=direct \
	--file-test-mode=rndrd run prepare

# 运行压测
$ sysbench  --threads=6 --time=0 --max-requests=0  fileio --file-num=1 \
	--file-total-size=10G --file-io-mode=sync --file-extra-flags=direct \
	--file-test-mode=rndrd run
...
Periodic FSYNC enabled, calling fsync() each 100 requests.
Calling fsync() at the end of test, Enabled.
Using synchronous I/O mode
Doing random read test
Initializing worker threads...

Threads started!

在运行 I/O 压测过程中,我们使用 top 命令查看 CPU 占用情况,可以看到 wa (I/O wait)值展示,图中为 38.3 %:

b53e1bc14c2e2acc911b355c1cff92dc

看到这里,给我们的感觉是 CPU I/O Wait 使用应该正确展示出了 I/O wait 的 CPU 使用情况。在我们一直保持 sysbench I/O 测试的同时,同时启动一个 CPU 密集类型的测试,然后我们观测 top 中的 wa 的值。这里测试我们设置使用所有的 CPU 核数。

1
$ sysbench --threads=`grep processor /proc/cpuinfo|wc -l` --time=0 cpu run

208a97d04ca69e6f6426d6641386cd2f

发生了什么,有没有觉得神奇,wa 变为了 0.0?给我们的感觉是 I/O 的瓶颈完全消失了?难道运行系统中的 I/O 压力突然消失了?我们可以注意到在运行的进程中两个 sysbench 进程在运行,分别对应于 I/O 和 CPU 测试的进程,所以实际运行情况看,I/O 的瓶颈仍然存在。

因此,I/O Wait 高能够表明系统中有许多进程在等待磁盘 I/O,但即使 I/O Wait 低甚至为 0,磁盘 I/O 也可能成为系统上某些进程的瓶颈。

结合我们上述的测试, I/O Wait 好像并不可靠,那么我们使用什么来提供更好 I/O 的可见性? 我们可以使用 vmstat 工具来进行查看。

在运行 I/O 和 CPU 负载的同时,我们使用 vmstat 工具观测,同时在一定时间测试后我们停掉 CPU 负载测试的 sysbench 进程,下图我们可以明显看到停止前后的效果对比趋势。

1b6bdde712f44cbbad2e97cc49c1eb8a

首先,我们可以发现 wa 列在运行 CPU 负载测试前后的变化比较明显,首列中的 r 的数据也得到了对应的体现(r 表示运行的任务数,运行 CPU 负载测试时的值为 12)。

15b628ce5fef0c35cc91208a3af1cef9

vmstat 展示数据中的 “b” 列代表的是对应于磁盘 I/O 上阻塞的进程,我们可以看到在运行 CPU 负载 sysbench 前后该值基本保持在 6 左右, 6 正是我们在运行 I/O 测试时候指定的 sysbench 中对应的 --threads=6 。该列值表明,即使在 wa 为 0.0 时,系统运行中仍然存在 6 个进程在等待 I/O。

fa1f24a7768d04a8dcb0dac2a419e01c

3. 进一步明确磁盘吞吐和读写频繁进程

在通过 vmstat b 列确定存在进程 I/O 等待情况后,我们也可以使用 iostat 和 iotop 进行进一步定义。

iostat 1 nvme0n1

nvme0n1 为本地磁盘

a921d64010d80eb7977092be6a3a3e8c

其中

  • tps:每秒事务数(IOPS)
  • kB_read/s、kB_wrtn/s : 每秒读取的 KB 数和每秒写入的 KB 数量

iotop 工具可快速定位到当前系统中读写频繁的进程信息。

3cacbc9f87bdce0a9fe755f60a3efbf2

4. 内核 CPU 统计实现分析

在通过上述分析以后,我们尝试从内核代码实现维度进行简单分析(内核代码代码版本 6.2.0 ):

cputime.c#L483

void account_process_tick(struct task_struct *p, int user_tick)
{
	u64 cputime, steal;

	if (vtime_accounting_enabled_this_cpu())
		return;

	if (sched_clock_irqtime) {
		irqtime_account_process_tick(p, user_tick, 1);
		return;
	}

	cputime = TICK_NSEC;
	steal = steal_account_process_time(ULONG_MAX);

	if (steal >= cputime)
		return;

	cputime -= steal;

  // 1. 如果当前进程处于用户态,那么增加用户态的 CPU 时间
	if (user_tick) 
		account_user_time(p, cputime);
  // 2. 如果前进程处于内核态,并且不是 idle 进程,那么增加内核态 CPU 时间
	else if ((p != this_rq()->idle) || (irq_count() != HARDIRQ_OFFSET))
		account_system_time(p, HARDIRQ_OFFSET, cputime);
  // 3. 如果当前进程是 idle 进程,那么调用 account_idle_time() 函数进行处理
	else
		account_idle_time(cputime);
}

account_idle_time 函数是统计当前 CPU 空闲的实现,可以看到如果 CPU 上有 nr_iowait 的值不为 0, 空余时间则会统计到 iowait 中,否则则会统计到 idle 列中。

cputime.c#L218

void account_idle_time(u64 cputime)
{
	u64 *cpustat = kcpustat_this_cpu->cpustat;
	struct rq *rq = this_rq();

  // 1. 如果当前有进程在等待 I/O 请求的话,那么增加 iowait的时间
	if (atomic_read(&rq->nr_iowait) > 0)
		cpustat[CPUTIME_IOWAIT] += cputime;
  // 2. 否则增加idle的时间
	else
		cpustat[CPUTIME_IDLE] += cputime;
}

通过代码我们可以明确看到,如果系统处于 I/O wait 状态,那么必须满足以下两个条件:

  1. 当前 CPU 中存在等待 I/O 请求完成的进程。
  2. 当期 CPU 处于空闲状态,也就是说没有可运行的进程。

5. 总结

经过上述测试,我们可以看到 I/O Wait 可能是一个令人非常困惑的指标。如果 CPU 密集型的进程运行,I/O Wait 的值可能会降低,但是尽管 I/O Wait 指标下降了,但磁盘 I/O 仍然和原来一样阻塞进程执行。所以在判断是否存在 I/O 瓶颈时,我们不能简单依靠 I/O Wait 值的高低来断定系统是否存在 I/O 瓶颈。

相信读完这篇文档的童鞋们,后续再也不会跳进 I/O Wait 指标的坑里了,那么这也就是本文的价值所在了。

<think>好的,我现在需要帮助用户解决技术瓶颈的问题,用户提到目前主要在做增删改查的业务代码,想提升技术水平。首先,我需要理解用户的具体情况。用户可能是一名初级或中级开发者,日常工作集中在基本的CRUD操作,感觉没有成长,想要突破。 接下来,我应该考虑用户可能的深层需求。用户可能希望从业务开发转向更高级的技术角色,或者希望提升自己的技术深度和广度,以便在职业发展上有更多机会。因此,我需要提供学习路径,涵盖底层原理、架构设计、性能优化、新技术探索等方面。 然后,我需要分析用户提到的增删改查工作,可能涉及的技术栈。比如,Java开发中的Spring框架、数据库优化、缓存技术等。用户可能需要学习如何优化现有代码,理解框架原理,或者学习分布式系统设计。 接下来,我需要制定一个结构化的学习计划。可能包括以下几个方面:深入理解底层原理,学习设计模式与架构,性能调优,接触云原生与分布式系统,拓宽技术视野,参与开源项目,以及建立知识体系。每个部分需要具体的学习方法和资源推荐。 同时,用户可能需要实际案例和练习来巩固知识,比如重构现有代码,模拟设计架构,参与开源项目等。这些实践能够帮助用户将理论知识应用到实际中,提升解决问题的能力。 还需要考虑用户的学习时间和精力,建议分阶段学习,避免一次性 overwhelmed。例如,先从底层原理开始,逐步过渡到架构设计,再学习分布式系统等高级主题。 另外,用户可能对如何平衡工作和学习有疑问,需要建议如何利用碎片时间,比如每天抽出固定时间学习,参与技术社区,写技术博客等。 最后,需要提醒用户保持耐心,技术提升是长期过程,鼓励持续学习和实践,同时关注行业动态,保持对新技术的敏感度。</think>以下是针对技术瓶颈突破的系统化提升方案,包含知识体系构建和实践路径设计: # 一、底层原理突破(解决"API调用工程师"困境) 1. **编程语言深度** - Java示例:通过字节码理解语法糖本质 ```java // 自动装箱陷阱(反编译后可见Integer.valueOf) Integer a = 127; Integer b = 127; System.out.println(a == b); // true(缓存范围) ``` 2. **框架源码学习** - Spring循环依赖解决流程图 ```mermaid graph TD A[创建Bean A] --> B[暴露ObjectFactory] B --> C[创建Bean B] C --> D[注入Bean A] D --> E[完成Bean B初始化] E --> F[完成Bean A初始化] ``` 3. **操作系统原理** - 用Go实现简易epoll模型: ```go func main() { listener, _ := syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, 0) event := syscall.EpollEvent{Events: syscall.EPOLLIN, Fd: int32(listener)} syscall.EpollCtl(epfd, syscall.EPOLL_CTL_ADD, listener, &event) for { events := make([]syscall.EpollEvent, 10) n, _ := syscall.EpollWait(epfd, events, -1) for i := 0; i < n; i++ { // 处理I/O事件 } } } ``` # 二、设计模式与架构演进(突破业务代码思维) 1. **模式场景化训练** - 策略模式在支付系统中的应用: ```java // 支付策略接口 interface PaymentStrategy { void pay(BigDecimal amount); } // 策略上下文 class PaymentContext { private PaymentStrategy strategy; public void executePayment(BigDecimal amount) { strategy.pay(amount); } } // 具体策略实现 class AlipayStrategy implements PaymentStrategy { @Override public void pay(BigDecimal amount) { // 调用支付宝SDK } } ``` 2. **DDD实战演练** - 领域事件设计示例: ```python class OrderShippedEvent: def __init__(self, order_id, shipping_info): self.order_id = order_id self.shipping_info = shipping_info class Order: def ship(self): # 领域逻辑 self.status = "SHIPPED" DomainEventPublisher.publish(OrderShippedEvent(self.id, self.address)) ``` 3. **架构演进路线** ``` 单机架构 → 服务拆分 → 微服务化 → 服务网格 │ │ │ │ │ ├─配置中心 ├─熔断降级 ├─Sidecar代理 │ └─日志收集 └─链路追踪 └─流量控制 ``` # 三、性能调优专项(从CRUD到系统优化) 1. **数据库深度优化** - B+树索引原理可视化: ``` [根节点] / | \ [叶子节点][...][叶子节点] 存储实际数据指针 ``` 2. **JVM调优实战** - GC日志分析模式: ``` [GC (Allocation Failure) [PSYoungGen: 130816K->1256K(153088K)] 130816K->1272K(502784K), 0.0023408 secs] ``` 3. **并发编程进阶** - 用Java实现无锁队列: ```java public class LockFreeQueue<T> { private AtomicReference<Node<T>> head, tail; public void enqueue(T value) { Node<T> newNode = new Node<>(value); while(true) { Node<T> last = tail.get(); Node<T> next = last.next.get(); if (last == tail.get()) { if (next == null) { if (last.next.compareAndSet(next, newNode)) { tail.compareAndSet(last, newNode); return; } } else { tail.compareAndSet(last, next); } } } } } ``` # 四、云原生与分布式系统(突破单机局限) 1. **分布式理论实践** - Raft协议动画演示地址:https://raft.github.io/ 2. **Service Mesh实践** - Istio流量镜像配置示例: ```yaml apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: reviews spec: hosts: - reviews http: - route: - destination: host: reviews subset: v1 mirror: host: reviews subset: v2 ``` 3. **Serverless案例** - AWS Lambda冷启动优化策略: ``` 保持函数精简 → 预置并发 → 避免VPC → 合理内存配置 ``` # 五、技术视野拓展(培养架构思维) 1. **基础组件研发** - 简易RPC框架实现步骤: ``` 协议定义 → 序列化 → 网络传输 → 动态代理 → 服务注册 ``` 2. **前沿技术跟踪** - Web3技术栈: ``` IPFS → 智能合约 → 零知识证明 → DAO治理 ``` 3. **效能提升工具** - 自定义代码生成器设计: ```javascript // 根据数据库表生成CRUD代码 function generate(model) { return ` class ${model.name}Controller { create(${model.name}Dto) { return this.service.create(${model.name}Dto); } }`; } ``` # 六、知识体系构建方法 1. **学习矩阵示例** | 维度 | 基础 | 进阶 | 专家级 | |------------|--------------------------|---------------------------|--------------------------| | 并发编程 | Thread/Runnable使用 | JUC包源码解析 | 自研调度框架 | | 分布式 | CAP理论 | Raft/Paxos实现 | 自研分布式协调服务 | 2. **费曼学习法实践** ``` 选择概念 → 教授他人 → 发现漏洞 → 重新梳理 → 简化表达 ``` 3. **刻意练习方案** - 数据库优化挑战: ```sql -- 给定10亿条订单数据 -- 要求:1秒内完成时间段+地区的统计查询 -- 设计:时序数据库+列式存储+预聚合 ``` **推荐学习路线**: ```mermaid gantt title 技术提升路线图 dateFormat YYYY-MM section 基础建设 操作系统原理 :done, 2023-01, 2023-03 网络协议深度 :active, 2023-04, 2023-06 section 架构进阶 分布式系统设计 :2023-07, 2023-09 云原生实践 :2023-10, 2024-01 section 领域深耕 金融系统架构 :2024-02, 2024-05 高并发场景优化 :2024-06, 2024-08 ``` **实践建议**: 1. 每周完成一个技术点源码分析(如Spring事务实现) 2. 每月模拟设计一个架构方案(如百万并发抽奖系统) 3. 参与开源项目贡献(从文档修改开始) 4. 建立个人技术博客(强制输出机制) 通过将20%时间投入底层原理研究+30%时间复杂场景实践+50%业务代码改造验证的方式,逐步实现从业务开发到系统架构师的能力跃迁。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值