一次服务器IO占用率高的定位分析

本文通过定位服务器磁盘高IO问题,发现jbd2进程占用IO过高,最终通过调整MySQL配置参数sync_binlog和innodb_flush_log_at_trx_commit解决了问题。

背景:请假在外中,听平台组同事反馈了一个问题,在往生产数据库中导入部分数据时会造成客户端的访问超时,初步定位是因为服务器磁盘占用IO过高,导数据时IO会飙升到100%,因此引起了不少数据库的慢查询操作导致客户端响应超时,无奈只好暂时停止了导入数据的脚本,同时也延误了针对这部分数据的生产测试工作。于是我第二天回到公司就投入了对这个问题的跟踪定位工作。


环境描述:

  • 操作系统

    wKioL1PzHbih3dckAABqlzu8NXs262.jpg

  • 文件系统

    wKiom1PzHLOyItieAADf6Fm1BEg633.jpg

  • 数据库

    wKiom1PzHMOg31Y7AADJcKOXs80235.jpg

首先我们数据库某最大表的数据也不过300w多条,对于MySQL来说还是能够正常处理的。而且客户端并发量也不过1K多,数据库的TPS也未过百,我先后使用了top,iostat监测到的IO利用率确实都已经达到极限了。最后使用iotop这个工具发现了一个吃IO的罪犯jbd2

wKioL1PzHf2g6lTuAADhxOHRieM865.jpg

Overview
The Journaling Block Device (JBD) provides a filesystem-independent interface for filesystem journaling. ext3, ext4 and OCFS2 are known to use JBD. OCFS2 starting from linux 2.6.28 and ext4 use a fork of JBD called JBD2.

可以知晓它的主要功能是向文件系统写日志。那么肯定是由于对文件系统的操作太频繁导致的IO压力过大,问题这是个系统进程,是系统问题还是?

目前我这台服务器上只有一个应用需要大量操作IO,那就是MySQL数据库,会不会由他导致的?怀着这个疑问我用google将mysql和jbd2联合作为关键字进行搜索,得到了这么一个线索sync_binlog,innodb_flush_log_at_trx_commit这两个mysql的配置项。顿时我仿佛想起了什么,于是翻到《高性能MySQL》这本书的第10章——复制的章节(从上面的环境描述中可以看到我使用了MySQL的主主复制)找到了对sync_binlog的说明

wKioL1PzHhLDWouAAAJT10pjdMs726.jpg

那么innodb_flush_log_at_trx_commit呢?

  • 如果innodb_flush_log_at_trx_commit设置为0,log buffer将每秒一次地写入log file中,并且log file的flush(刷到磁盘)操作同时进行.该模式下,在事务提交的时候,不会主动触发写入磁盘的操作。

  • 如果innodb_flush_log_at_trx_commit设置为1,每次事务提交时MySQL都会把log buffer的数据写入log file,并且flush(刷到磁盘)中去.

  • 如果innodb_flush_log_at_trx_commit设置为2,每次事务提交时MySQL都会把log buffer的数据写入log file.但是flush(刷到磁盘)操作并不会同时进行。该模式下,MySQL会每秒执行一次 flush(刷到磁盘)操作。

由于我们的业务数据的特点,对数据可靠性并不如金融、订单系统那么高于是在权衡下就把sync_binlog设置为每500次刷新一次磁盘,而将innodb_flush_log_at_trx_commit设置为2,再用iotop等工具查看系统IO情况,大大降了下来。好吧,这个借刀杀IO的罪犯终于找到并被处理了。

后记:在这次处理问题的过程中有两个小插曲。

  1. 某领导找来几个人对此问题进行会诊,有猜测服务器资源不够的,有猜测脚本问题的,有猜测数据库本身效率底下的…我个人非常非常反感在没有经过性能监控和数据的分析而凭空猜测问题的做法。我希望给所有靠凭空想象定位问题的人提个醒,请不要随意对自己不了解的问题定性,因为别人不会把你看做高手,只会对你视而不见。

  2. 在找出jbd2的问题之后,看到一些论坛解决方案说是由于linux内核的bug可以选择升级系统内核或者修改内核配置项来解决,也许是对的,但是即使能解决这个问题对我来说成本也很大。我希望大家遇到问题时在利用网络资源的同时结合自己的情况进行进一步分析再选择采用什么解决方案,适合自己的才是最好的

参考资料:
http://unix.stackexchange.com/questions/86875/determining-specific-file-responsible-for-high-i-o
http://serverfault.com/questions/363355/io-wait-causing-so-much-slowdown-ext4-jdb2-at-99-io-during-mysql-commit
http://blog.itpub.net/22664653/viewspace-1063134/


<think>嗯,用户正在询问如何排查和解决CPU占用率的问题。这确实是一个常见的系统性能问题。用户可能是一位开发者或运维人员,遇到了服务器性能瓶颈,或者正在准备面试——因为引用[1]提到面试官问过这个问题。 从用户提供的引用材料来看,ta已经做了一些基础调研,但需要更系统的排查方法。引用[1]提到Linux系统的排查经验,引用[2]给出Java进程的具体操作步骤,引用[3]则展示了一个线程死循环的代码案例。这些材料质量参差不齐,需要整合成逻辑清晰的排查流程。 用户的核心诉求是"如何排查和解决",因此回复需要突出可操作性。应该从宏观到微观设计排查路径:先定位进程,再定位线程,最后分析代码。每个步骤都要给出具体命令和解读方法,比如top命令的交互操作(引用[2]提到的Shift+</>切换亮列很实用)。 特别要注意几个技术细节:1) Java进程需要用jstack配合线程ID转换(十六进制转换容易出错);2) 死循环和死锁的表现差异(CPU vs 等待);3) 容器化环境(如Docker)的排查差异。用户没提环境细节,所以需要涵盖通用场景。 解决方案部分要区分应急处理和根治措施。像引用[3]的线程睡眠方案只能算临时规避,更优解应该是用ScheduledExecutorService。对于算法优化,可以举斐波那契数列的例子说明递归优化空间。 最后考虑补充两个实用技巧:1) perf工具做CPU火焰图定位热点函数 2) 用cgroups限制进程资源。虽然这些进阶内容可能超出用户当前需求,但对阶用户有价值。 准备在相关问题里埋个伏笔——如果用户后续追问,可以展开讲解异步编程优化或容器诊断方案。现在先把回复结构拆解成诊断流程、解决方案、预防措施三部分,确保每个命令都有示例输出和解读逻辑。</think>排查CPU占用率问题需要系统化诊断,以下是详细步骤和解决方案: --- ### 🔍 一、诊断流程(Linux系统) #### 1. **定位CPU进程** ```bash top # 实时查看进程资源占用 ``` - **操作技巧**: - 按 `1`:显示所有CPU核心的独立使用率 - 按 `Shift + P`:按CPU占用排序 - 按 `c`:显示进程完整命令 - 记录CPU进程的 **PID**(如Java进程PID为1234) #### 2. **分析进程内线程** ```bash top -H -p 1234 # 查看指定进程的所有线程 ``` - 记录CPU线程的 **十六进制PID**(如线程PID 5678 的十六进制为 `0x162E`) #### 3. **线程堆栈分析(Java示例)** ```bash jstack 1234 > thread_dump.txt # 生成线程快照 ``` - 在 `thread_dump.txt` 中搜索 `nid=0x162E`(对应线程PID 5678),定位线程堆栈 #### 4. **检查系统调用** ```bash perf top -p 1234 # 实时监控进程系统调用 strace -p 5678 -c # 统计线程系统调用(替换5678为实际线程PID) ``` - 关注频繁调用的系统函数(如文件读写、网络请求) #### 5. **辅助工具** - **火焰图**:可视化CPU时间消耗 ```bash perf record -F 99 -p 1234 -g -- sleep 30 perf script | FlameGraph/stackcollapse-perf.pl | FlameGraph/flamegraph.pl > flame.svg ``` - **监控工具**:`vmstat 1`(查看上下文切换)、`pidstat -t -p 1234 1`(线程级监控) --- ### 🛠️ 二、常见原因与解决方案 | **问题类型** | **典型表现** | **解决方案** | |---------------------|----------------------------|---------------------------------------| | **死循环** | 线程持续占用CPU(如引用[3])| 检查循环退出条件,添加`Thread.sleep()` | | **锁竞争** | 大量线程处于`BLOCKED`状态 | 优化锁粒度,改用并发集合类 | | **算法低效** | 递归或复杂计算占用CPU | 优化算法(如缓存中间结果) | | **频繁GC** | `gc.log`显示Full GC频繁 | 调整JVM参数,分析内存泄漏 | | **IO阻塞** | `iowait`(`top`可见) | 优化磁盘读写,使用异步IO | | **第三方库缺陷** | 特定库版本线程异常 | 升级依赖库或替换实现 | --- ### 🛡️ 三、预防措施 1. **代码层面**: - 避免在循环中同步阻塞操作 - 使用线程池控制并发量 - 复杂计算添加超时中断机制 2. **监控层面**: ```bash # 定期记录资源使用 crontab -e */5 * * * * pidstat -u -p ALL 1 60 > /monitor/cpu_$(date +\%F).log ``` 3. **压测验证**: - 使用JMeter/Siege模拟并发场景 - 对比优化前后的CPU使用率曲线 > 💡 **关键提示**:容器环境(如Docker)需在宿主机执行 `top` 或 `perf` 时添加 `--cap-add SYS_ADMIN` 权限[^1]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值