服务器磁盘读写效率,网络吞吐量查看,mysql性能调优

文章目录

一、概况

在日常的大数据,高并发的项目中,需要对服务器cpu,内存,磁盘io,网络io进行分析,找出瓶颈。我这里结合我的一个实际项目介绍我常用的工具。

最初的问题是车辆的接入数据比实际慢了2小时。

实际数据我们存放到kafka是及时的,问题出在kafka往数据库mysql写的时候慢了。到底是消费跟不上还是写入数据库跟不上要确认。也就是说,要增加kafka的分区还是要优化数据库。这是我们的关键。

二、工具集合

  • htop
  • top
  • iotop 看磁盘读写,定位到进程
  • nethogs 看网络,定位到进程
  • iostat 看哪个磁盘,分析效率(大文件还是小文件)

三、快速总结

  • 看整体用htop,top
  • 看细节用iotop,nethogs,iostat

(1)htop

配置

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

解读(1)

  1. CPU 严重倾斜,双核忙双核闲
    CPU0、CPU1 占用 30% 左右;CPU2、CPU3 只有 5% 上下。
    整机只有2 个 running 任务,其余几百线程全部 sleep,和之前 top 观察完全一致。

Java 大量线程处于 WAITING/TIMED_WAITING,同一时刻真正可运行线程很少,无法把 4 核全部打满。

  1. 内存:总 32G,使用 12.6G,剩余充足。Swap 使用 857M,有少量换出,需要关注但不算严重。
  2. Disk I/O 几乎为 0:这台应用服务器本身没有磁盘 IO 瓶颈,压力不在本机磁盘。
  3. 网络吞吐:收发 2MiB/s 左右,流量不算大。

解读(2)

这台是MySQL服务器

现象解读
  1. CPU集中压在CPU0、CPU1,另外两个核空闲,MySQL大量工作线程抢在两个核心上;系统整体只有2个处于running状态线程,其余大量线程sleep等待磁盘IO。
  2. Disk I/O 77.2% 和之前iostat看到vdb %util 75%完全对上,磁盘写压力大。写入速度只有3MB/s,是大量小随机写,不是大文件顺序写。
  3. 内存:16G总内存,只用6.4G,内存充足,swap仅277M,内存不是瓶颈。
  4. 网络吞吐不高,说明不是大结果集返回,是频繁小请求。
  5. load 1.69‑2.31,4核机器,负载不算爆表,但大量线程等待IO。
整套故障链路复盘
  1. MySQL侧:InnoDB大量小随机写(redo + 脏页刷盘),磁盘vdb设备util75%,iowait14%。大量MySQL线程等待磁盘完成IO,进入sleep。
  2. Java应用侧:JDBC请求发往MySQL,在数据库这边被IO阻塞,应用线程全部WAITING,应用机器CPU跑不满多核。
  3. 表现出来:业务接口慢,应用服务器CPU上不去,数据库CPU只有部分核高、磁盘util高。
需要确认的关键点
  1. 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刷脏,产生巨量小随机写。

  1. 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再启动,直接改配置不删文件会启动失败

  1. 慢查询日志,抓update/insert,看是不是高频小事务。
下一步动作清单
  1. 导出show engine innodb status\G完整输出。
  2. 看慢查询,统计高频写SQL。
  3. 评估业务,确认是否可以把innodb_flush_log_at_trx_commit=2
  4. 规划停机窗口调整innodb_log_file_size=2G
  5. 应用侧线程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

关键结论

  1. 磁盘写流量全部来自mysqld线程 ,读几乎为0,纯写压力,写速度3‑4MB/s,速率不高,但IO%被占住,这就是典型大量小随机IO。
  2. 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内部等待,业务慢。

下一步执行顺序(优先级)

  1. 拿到 show engine innodb status\G,重点观察:

    • Modified db pages(脏页数量)
    • Log sequence number / Log flushed up to,看checkpoint有没有追赶压力
    • Pending flushes
  2. 查看当前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缓存
  1. 业务评估innodb_flush_log_at_trx_commit
  • =1:每次事务落盘,最安全,IO压力最大
  • =2:每秒刷一次redo,崩溃最多丢1秒数据,写性能大幅提升;如果业务允许,优先改成2缓解压力。

⚠️修改innodb_log_file_size操作注意:

  1. 正常关闭MySQL

  2. 删除数据目录下 ib_logfile0 ib_logfile1

  3. 修改my.cnf

  4. 启动MySQL,会重建新日志文件;不删旧文件会直接启动失败。

  5. 应用侧抓线程dump,确认大量线程卡在JDBC MySQL调用,完成闭环。

临时应急(不停机)

  • 调高innodb_io_capacityinnodb_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

结论

  1. 网络不是瓶颈,流量只有2MB/s级别,没有打满网卡。业务慢不是网络带宽导致。
  2. 大量短连接/并发MySQL会话,应用服务器和DB之间交互频繁,但报文量不大,就是大量小请求。
  3. 和前面磁盘监控互相印证:
    • 网络流量不大 → 不是返回大结果集;
    • 压力落在数据库本机磁盘随机IO(mysqld的小IO刷脏页)。

完整故障全景汇总

  1. 网络层:应用 ↔ MySQL,流量低,网络正常,无瓶颈。
  2. MySQL磁盘层:iostat %util75%、iotop确认全部写IO来自mysqld,吞吐仅3‑4MB/s,大量4K随机写。
  3. MySQL主机CPU:仅2个核心负载高,其余空闲,大量线程阻塞等待IO。
  4. 应用服务器:CPU跑不满,大量线程WAITING,在等待MySQL返回。

根因高度指向 InnoDB redo日志文件innodb_log_file_size过小,checkpoint强制刷脏页产生大量随机IO

接下来必做

  1. 在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';
  1. 获取完整输出:
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步骤:

  1. 正常关闭MySQL
  2. 删除数据目录 ib_logfile0ib_logfile1
  3. my.cnf配置 innodb_log_file_size = 2G
  4. 启动MySQL,数据库自动生成新日志文件。

不删除旧日志直接改配置,MySQL无法启动

应用端验证

抓Java线程dump,确认大量业务线程栈卡在mysql jdbc socket等待,完成证据闭环。

(4)iostat

bash 复制代码
yum install sysstat -y
iostat -x 2

看磁盘%utilawait,判断磁盘硬件是否打满。

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基本一致。

📊关键结论

  1. 不是吞吐量打满,是IOPS压力
    写入流量只有3MB/s很小,但每秒479个写IO,属于大量小随机写 ,典型MySQL InnoDB刷redo、脏页刷新。磁盘%util=75%,设备已经比较忙。
  2. w_await=0.61ms,单次写本身延迟尚可;但队列堆积,整体CPU iowait被拉高。
  3. vda几乎没有IO,系统盘空闲。

现象:小IO多,带宽不大,设备util到75%,iowait14%,是InnoDB刷盘的典型表现。

对应MySQL调优方向

  1. 增大 innodb_log_file_size

    redo日志太小会频繁轮转触发大量刷脏页,建议调整为2G(重启生效),减少频繁checkpoint产生大量小写。

  2. 调整脏页刷盘参数

ini 复制代码
innodb_io_capacity = 2000
innodb_io_capacity_max = 4000
innodb_max_dirty_pages_pct_lwm=10
  1. 如果业务允许,innodb_flush_log_at_trx_commit=2,大幅降低redo同步写压力(掉电最多丢1秒数据)。

  2. innodb_flush_method=O_DIRECT,绕过OS pagecache,减少操作系统缓存带来的刷盘抖动。

  3. 检查慢查询日志,看有没有大量更新语句,导致大量脏页产生。

操作系统侧

观察 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 checkpoint 2164099663571
    计算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空闲充足。

结合全部监控复盘现状

  1. innodb状态:脏页少、内存命中率100%,无大量pending write;
  2. iostat:磁盘%util高,但是吞吐只有3‑4MB/s;
  3. iotop:写IO全部来自mysqld;
  4. nethogs:网络压力不大,应用大量小请求访问MySQL;
  5. htop观察MySQL线程大量sleep等待IO。

当前可能性排序

  1. 磁盘本身随机IO性能短板(云盘/机械盘)
    虽然写入总流量只有3‑4MB/s,但这是大量离散的4K随机写(undo、binlog、redo刷盘、少量数据页),云磁盘IOPS配额打满,导致%util高。

%util=设备繁忙时间,不是吞吐量;低MB/s,高IOPS就会出现该现象。

  1. binlog刷盘策略

    sync_binlog=1:每次事务binlog强制落盘,大量小事务,产生大量离散IO。

  2. 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。

快速验证建议

  1. 如果业务可接受,临时调整
sql 复制代码
set global sync_binlog=1000;
set global innodb_flush_log_at_trx_commit=2;

会减少大量fsync调用,观察磁盘%util是否下降。⚠️崩溃会丢少量数据。

执行这两句后磁盘的繁忙从75%直接下降到7%。所以问题找到了。

繁忙降低,大小7M

  1. 确认磁盘类型:是SSD云盘还是机械盘;看云平台的磁盘IOPS上限,是否被限流。

  2. 看binlog文件生成速度,是不是大量小事务疯狂生成binlog。

补充

现在脏页很少,不需要调大innodb_log_file_size,这个方向可以暂停。瓶颈不在buffer pool刷脏,而在大量小事务的fsync(redo/binlog同步刷盘)带来的随机IO压力

现象:业务发很多小事务,每个事务都要求强制磁盘刷写,数据量不大,但系统调用fsync非常频繁,磁盘设备忙。

相关推荐
张小姐的猫2 小时前
【Linux】网络编程 —— 传输层协议 TCP(下)
linux·运维·服务器·网络·tcp/ip·http·php
Light Gao2 小时前
企业级灰度发布技术方案
网络·数据库·oracle
Little Tian3 小时前
基于FPGA的UDP回环实验(二)----ARP模块
网络·网络协议·udp
程序员AlbertTu3 小时前
第2章 上手 Linux:环境与文件操作
linux·运维·服务器
今儿敲了吗3 小时前
CN——数据链路层(下)
网络·笔记
你怎么知道我是队长3 小时前
计算机网络入门指南:从OSI模型到网络设备
网络·计算机网络
K成长日志3 小时前
BLE链路层--比特流处理
网络·物联网·网络协议·蓝牙·iot·ble·无线
怪奇云呼军3 小时前
闪电智能VoiceAgent 如何管理呼入、接听、桥接和挂断状态?
java·前端·网络·数据库·人工智能
予昊3 小时前
Linux 资源控制实战分析:用 LXC 亲手造一个 “迷你虚拟机“
linux·运维·服务器