文章目录
- 一、概况
- 二、工具集合
- 三、快速总结
-
- (1)htop
- (2)iotop
- (3)nethogs
- (4)iostat
-
- [iostat结果解读(MySQL机器 vdb/dm‑0)](#iostat结果解读(MySQL机器 vdb/dm‑0))
- [磁盘设备 vdb(数据库盘)](#磁盘设备 vdb(数据库盘))
- 📊关键结论
- 对应MySQL调优方向
- 操作系统侧
- 快速区分
- 四、最终解决
-
- 解决方法
- [show engine innodb status 片段解读](#show engine innodb status 片段解读)
-
- LOG(redo日志区)
- [Buffer Pool内存区](#Buffer Pool内存区)
- 推翻之前猜想
- 结合全部监控复盘现状
- 当前可能性排序
- 需要查询的参数
- 快速验证建议
- 补充
一、概况
在日常的大数据,高并发的项目中,需要对服务器cpu,内存,磁盘io,网络io进行分析,找出瓶颈。我这里结合我的一个实际项目介绍我常用的工具。
最初的问题是车辆的接入数据比实际慢了2小时。

实际数据我们存放到kafka是及时的,问题出在kafka往数据库mysql写的时候慢了。到底是消费跟不上还是写入数据库跟不上要确认。也就是说,要增加kafka的分区还是要优化数据库。这是我们的关键。
二、工具集合
- htop
- top
- iotop 看磁盘读写,定位到进程
- nethogs 看网络,定位到进程
- iostat 看哪个磁盘,分析效率(大文件还是小文件)
三、快速总结
- 看整体用htop,top
- 看细节用iotop,nethogs,iostat
(1)htop
配置

红框的部分怎么调出来呢,按F2进到下面这个界面

解读(1)

- CPU 严重倾斜,双核忙双核闲
CPU0、CPU1 占用 30% 左右;CPU2、CPU3 只有 5% 上下。
整机只有2 个 running 任务,其余几百线程全部 sleep,和之前 top 观察完全一致。
Java 大量线程处于 WAITING/TIMED_WAITING,同一时刻真正可运行线程很少,无法把 4 核全部打满。
- 内存:总 32G,使用 12.6G,剩余充足。Swap 使用 857M,有少量换出,需要关注但不算严重。
- Disk I/O 几乎为 0:这台应用服务器本身没有磁盘 IO 瓶颈,压力不在本机磁盘。
- 网络吞吐:收发 2MiB/s 左右,流量不算大。
解读(2)
这台是MySQL服务器

现象解读
- CPU集中压在CPU0、CPU1,另外两个核空闲,MySQL大量工作线程抢在两个核心上;系统整体只有2个处于running状态线程,其余大量线程sleep等待磁盘IO。
- Disk I/O 77.2% 和之前iostat看到vdb
%util 75%完全对上,磁盘写压力大。写入速度只有3MB/s,是大量小随机写,不是大文件顺序写。 - 内存:16G总内存,只用6.4G,内存充足,swap仅277M,内存不是瓶颈。
- 网络吞吐不高,说明不是大结果集返回,是频繁小请求。
- load 1.69‑2.31,4核机器,负载不算爆表,但大量线程等待IO。
整套故障链路复盘
- MySQL侧:InnoDB大量小随机写(redo + 脏页刷盘),磁盘vdb设备util75%,iowait14%。大量MySQL线程等待磁盘完成IO,进入sleep。
- Java应用侧:JDBC请求发往MySQL,在数据库这边被IO阻塞,应用线程全部WAITING,应用机器CPU跑不满多核。
- 表现出来:业务接口慢,应用服务器CPU上不去,数据库CPU只有部分核高、磁盘util高。
需要确认的关键点
show engine innodb status\G
重点看这几段:
Modified db pages(脏页数量)Log sequence number/Log flushed up to(redo日志checkpoint追赶情况)Pending writes,看有没有大量pending flush。
如果脏页很多,checkpoint追不上,就是典型:
innodb_log_file_size太小,频繁触发checkpoint刷脏,产生巨量小随机写。
- my.cnf检查关键参数
ini
innodb_log_file_size = 2G # 现在大概率是默认512M,过小
innodb_flush_method = O_DIRECT
innodb_flush_log_at_trx_commit = 1 # 如果业务可接受丢1s,改成2减轻写压力
innodb_io_capacity = 2000
innodb_io_capacity_max = 4000
修改
innodb_log_file_size需要关闭MySQL,删除旧的ib_logfile0/ib_logfile1再启动,直接改配置不删文件会启动失败。
- 慢查询日志,抓update/insert,看是不是高频小事务。
下一步动作清单
- 导出
show engine innodb status\G完整输出。 - 看慢查询,统计高频写SQL。
- 评估业务,确认是否可以把
innodb_flush_log_at_trx_commit=2。 - 规划停机窗口调整
innodb_log_file_size=2G。 - 应用侧线程dump确认大量线程栈卡在mysql jdbc调用,闭环确认。
这一套现象非常典型,不是磁盘硬件坏,是InnoDB日志文件配置偏小,导致checkpoint压力。
(2)iotop
bash
yum install iotop -y
# 只看有IO活动的进程
iotop -oP

iotop输出解读
Total DISK READ: 0.00 B/s | Total DISK WRITE:3.61 M/s
Current DISK READ:0.00 B/s | Current DISK WRITE:3.93 M/s
TID PRIO USER DISK READ DISK WRITE SWAPIN IO COMMAND
3837452 be/4 systemd‑c 0B/s 3.61M/s 0% 1.78% mysqld
关键结论
- 磁盘写流量全部来自mysqld线程 ,读几乎为0,纯写压力,写速度3‑4MB/s,速率不高,但IO%被占住,这就是典型大量小随机IO。
- IO%只有1.78%,说明单个线程瞬时IO不猛,是大量分散的小IO请求排队,所以iostat看到%util 75%,但吞吐只有3MB/s。
%util是设备繁忙时间占比,不是吞吐量;大量4K随机写,即使总MB不大,磁盘设备也会跑满繁忙度。
和前面全部监控闭环
- iostat:vdb %util≈75%,w/s很高,写吞吐3MB/s,iowait14%
- htop:MySQL机器磁盘IO77%,CPU只有2个核忙,大量线程sleep等待IO
- iotop:确认全部写IO来源就是mysqld,没有其他进程抢磁盘
👉 现象完全符合 InnoDB checkpoint压力(redo日志太小) :
redo日志不够大,InnoDB必须不断刷脏页到磁盘,产生大量小随机写,实际写入流量不大,但IOPS打上去,磁盘设备忙,造成MySQL内部等待,业务慢。
下一步执行顺序(优先级)
-
拿到
show engine innodb status\G,重点观察:- Modified db pages(脏页数量)
- Log sequence number / Log flushed up to,看checkpoint有没有追赶压力
- Pending flushes
-
查看当前
my.cnf
ini
#看现在日志大小
show variables like 'innodb_log_file_size';
show variables like 'innodb_flush_method';
show variables like 'innodb_flush_log_at_trx_commit';
- 如果
innodb_log_file_size是512M,就是根因,需要调到2G(需要停机操作) innodb_flush_method必须是O_DIRECT,不要fsync缓存
- 业务评估
innodb_flush_log_at_trx_commit
- =1:每次事务落盘,最安全,IO压力最大
- =2:每秒刷一次redo,崩溃最多丢1秒数据,写性能大幅提升;如果业务允许,优先改成2缓解压力。
⚠️修改
innodb_log_file_size操作注意:
-
正常关闭MySQL
-
删除数据目录下 ib_logfile0 ib_logfile1
-
修改my.cnf
-
启动MySQL,会重建新日志文件;不删旧文件会直接启动失败。
-
应用侧抓线程dump,确认大量线程卡在JDBC MySQL调用,完成闭环。
临时应急(不停机)
- 调高
innodb_io_capacity、innodb_io_capacity_max,给刷脏更多IO配额,只能缓解,不能根治日志过小的问题。
sql
set global innodb_io_capacity=4000;
set global innodb_io_capacity_max=8000;
补充区分
- 如果是大吞吐顺序写:MB/s会很高几十MB以上
- 当前场景:低吞吐、高IOPS、高%util,就是InnoDB脏页刷盘的典型特征。
(3)nethogs
bash
yum install nethogs -y
# 按进程看网络流量
nethogs

关键信息
- 大量
docker‑proxy进程,容器之间通信,端口 3306 / 3307(MySQL) - 总接收:2336 KB/s ≈2.3MB/s,发送817KB/s
- 通信双方:
- Java应用:
10.101.2.36 - MySQL数据库:
10.101.2.142
- Java应用:
结论
- 网络不是瓶颈,流量只有2MB/s级别,没有打满网卡。业务慢不是网络带宽导致。
- 大量短连接/并发MySQL会话,应用服务器和DB之间交互频繁,但报文量不大,就是大量小请求。
- 和前面磁盘监控互相印证:
- 网络流量不大 → 不是返回大结果集;
- 压力落在数据库本机磁盘随机IO(mysqld的小IO刷脏页)。
完整故障全景汇总
- 网络层:应用 ↔ MySQL,流量低,网络正常,无瓶颈。
- MySQL磁盘层:iostat %util75%、iotop确认全部写IO来自mysqld,吞吐仅3‑4MB/s,大量4K随机写。
- MySQL主机CPU:仅2个核心负载高,其余空闲,大量线程阻塞等待IO。
- 应用服务器:CPU跑不满,大量线程WAITING,在等待MySQL返回。
根因高度指向 InnoDB redo日志文件
innodb_log_file_size过小,checkpoint强制刷脏页产生大量随机IO。
接下来必做
- 在MySQL执行SQL看当前参数
sql
show variables like 'innodb_log_file_size';
show variables like 'innodb_flush_method';
show variables like 'innodb_flush_log_at_trx_commit';
- 获取完整输出:
sql
show engine innodb status\G
重点看脏页数量、checkpoint追赶情况。
应急缓解(在线执行,不用停机)
sql
set global innodb_io_capacity=4000;
set global innodb_io_capacity_max=8000;
这只能临时给刷脏分配更多IO,不能根治;根治需要调大
innodb_log_file_size(需要停机删ib_logfile文件)。
业务评估
- 如果业务允许最多丢失1秒数据,可以设置
sql
set global innodb_flush_log_at_trx_commit=2;
大幅降低每次事务刷盘压力。
永久修复操作提醒
修改innodb_log_file_size步骤:
- 正常关闭MySQL
- 删除数据目录
ib_logfile0、ib_logfile1 - my.cnf配置
innodb_log_file_size = 2G - 启动MySQL,数据库自动生成新日志文件。
不删除旧日志直接改配置,MySQL无法启动。
应用端验证
抓Java线程dump,确认大量业务线程栈卡在mysql jdbc socket等待,完成证据闭环。
(4)iostat
bash
yum install sysstat -y
iostat -x 2
看磁盘%util、await,判断磁盘硬件是否打满。

iostat结果解读(MySQL机器 vdb/dm‑0)
avg‑cpu: %user 20.68, %nice 2.88, %system 23.56, %iowait 14.40, %steal 0.00, %idle 38.48
iowait=14.4%,已经是明显IO等待,CPU大量时间在等磁盘完成IO
磁盘设备 vdb(数据库盘)
| 指标 | 数值 | 含义 |
|---|---|---|
| w/s | 479.00 | 每秒写请求479次 |
| wkB/s | 3099.75 KB/s ≈3MB/s | 写入吞吐量不大,但IOPS很高 |
| r_await | 1.28ms | 读延迟很好 |
| w_await | 0.61ms | 写单次IO延迟很低 |
| %util | 75.35% | 磁盘设备繁忙度75% |
dm‑0是vdb对应的LVM逻辑卷,数值和vdb基本一致。
📊关键结论
- 不是吞吐量打满,是IOPS压力
写入流量只有3MB/s很小,但每秒479个写IO,属于大量小随机写 ,典型MySQL InnoDB刷redo、脏页刷新。磁盘%util=75%,设备已经比较忙。 w_await=0.61ms,单次写本身延迟尚可;但队列堆积,整体CPU iowait被拉高。- vda几乎没有IO,系统盘空闲。
现象:小IO多,带宽不大,设备util到75%,iowait14%,是InnoDB刷盘的典型表现。
对应MySQL调优方向
-
增大
innodb_log_file_sizeredo日志太小会频繁轮转触发大量刷脏页,建议调整为
2G(重启生效),减少频繁checkpoint产生大量小写。 -
调整脏页刷盘参数
ini
innodb_io_capacity = 2000
innodb_io_capacity_max = 4000
innodb_max_dirty_pages_pct_lwm=10
-
如果业务允许,
innodb_flush_log_at_trx_commit=2,大幅降低redo同步写压力(掉电最多丢1秒数据)。 -
innodb_flush_method=O_DIRECT,绕过OS pagecache,减少操作系统缓存带来的刷盘抖动。 -
检查慢查询日志,看有没有大量更新语句,导致大量脏页产生。
操作系统侧
观察 iotop -oP,确认IO主要来自mysqld进程。
当前磁盘还没有到100%util完全卡死状态,但高峰期会继续上涨,iowait进一步抬升,会造成上层Java应用数据库调用阻塞,线程sleep。
快速区分
- 如果
await > 20ms:磁盘硬件压力很大,考虑升级SSD; - 你这里await很低,主要是MySQL脏页刷盘策略导致大量小随机写,优先调MySQL参数。
如果你把 show engine innodb status\G 的输出贴出来,我可以直接看脏页数量、checkpoint情况。
四、最终解决
解决方法
从以上各方面分析,终的关键点是,磁盘很忙,但是写入数据量不大。问题就是mysql
sql
set global sync_binlog=1000;
set global innodb_flush_log_at_trx_commit=2;
磁盘IO直接从75%降低到6%,看下面的分析。
show engine innodb status 片段解读
执行 show engine innodb status\G
重点看这几段:
Modified db pages(脏页数量)Log sequence number/Log flushed up to(redo日志checkpoint追赶情况)Pending writes,看有没有大量pending flush。
如果脏页很多,checkpoint追不上,就是典型:
innodb_log_file_size太小,频繁触发checkpoint刷脏,产生巨量小随机写。

往下拉找到

LOG(redo日志区)
Log sequence number 2164107953107
Log flushed up to 2164107952546
Pages flushed up to 2164099703472
Last checkpoint at 2164099663571
1 pending log flushes, 0 pending chkp writes
3832348220 log i/o's done, 194.44 log i/o's/second
- LSN(日志序列号)
2164107953107,Last checkpoint2164099663571
计算checkpoint距离(LSN差值):
LSN差值 = 8289536 bytes ≈7.9MB
redo日志产生了7.9MB还没checkpoint落盘,这个差值很小 ,说明不是redo日志满触发的激进checkpoint,排除"日志文件太小导致刷脏风暴"这个经典场景。
1 pending log flushes:有1个redo日志等待刷盘;- log i/o 194次每秒,redo本身压力不算巨大。
Buffer Pool内存区
Buffer pool size 655360 # 单位pages,page=16K → 总BP大小
Free buffers 306956 #空闲buffer页非常多
Database pages 347991
Modified db pages 1486 # 脏页数量只有1486个,1486*16K ≈ 23.2MB
Pending writes: LRU 0, flush list 0, single page 0
- Buffer Pool总大小:
655360 *16KB = 10240 MB =10GB - 脏页仅 1486页 ≈23MB,脏页量很低,没有大量脏页堆积。
- Free buffers:306956,大量空闲页,内存充足,不存在LRU淘汰压力。
- Pending writes全部0:没有排队的刷脏任务。
- Buffer pool hit rate 1000/1000 =100%,全部命中内存,磁盘读几乎没有。
推翻之前猜想
❌不是redo日志过小、不是脏页堆积、不是checkpoint风暴 。
脏页很少,checkpoint LSN差距只有7.9MB,BP空闲充足。
结合全部监控复盘现状
- innodb状态:脏页少、内存命中率100%,无大量pending write;
- iostat:磁盘%util高,但是吞吐只有3‑4MB/s;
- iotop:写IO全部来自mysqld;
- nethogs:网络压力不大,应用大量小请求访问MySQL;
- htop观察MySQL线程大量sleep等待IO。
当前可能性排序
- 磁盘本身随机IO性能短板(云盘/机械盘)
虽然写入总流量只有3‑4MB/s,但这是大量离散的4K随机写(undo、binlog、redo刷盘、少量数据页),云磁盘IOPS配额打满,导致%util高。
%util=设备繁忙时间,不是吞吐量;低MB/s,高IOPS就会出现该现象。
-
binlog刷盘策略
sync_binlog=1:每次事务binlog强制落盘,大量小事务,产生大量离散IO。 -
innodb_flush_log_at_trx_commit=1,每次事务redo落盘;大量短事务,每个事务都fsync。
需要查询的参数
sql
show variables like 'sync_binlog';
show variables like 'innodb_flush_log_at_trx_commit';
show variables like 'innodb_flush_method';
show variables like 'binlog_format';
show global status like 'Com_commit';
- Com_commit:看每秒提交事务数量,如果每秒几百上千小事务,即使数据量很小,也会制造大量fsync系统调用,压垮随机IO。
快速验证建议
- 如果业务可接受,临时调整
sql
set global sync_binlog=1000;
set global innodb_flush_log_at_trx_commit=2;
会减少大量fsync调用,观察磁盘%util是否下降。⚠️崩溃会丢少量数据。
执行这两句后磁盘的繁忙从75%直接下降到7%。所以问题找到了。
繁忙降低,大小7M
-
确认磁盘类型:是SSD云盘还是机械盘;看云平台的磁盘IOPS上限,是否被限流。
-
看binlog文件生成速度,是不是大量小事务疯狂生成binlog。
补充
现在脏页很少,不需要调大innodb_log_file_size,这个方向可以暂停。瓶颈不在buffer pool刷脏,而在大量小事务的fsync(redo/binlog同步刷盘)带来的随机IO压力。
现象:业务发很多小事务,每个事务都要求强制磁盘刷写,数据量不大,但系统调用fsync非常频繁,磁盘设备忙。
