【Linux】系统性能排查:解决卡顿问题
本专栏文章持续更新,新增内容使用蓝色表示。
一、基本必知
1.1 负载平均值
该值为准备运行的进程数(标志位为R)或等待I/O完成的进程数(标志位为D)的运行平均值。由CPU、磁盘、网络决定,响应时间0.1s以下(亚秒级响应)。
显示当前负载平均值:
uptime
三个值分别代表最近1、5、15分钟的负载情况。
负载值 < CPU 核心数:系统相对空闲,资源充足。
≈ CPU 核心数:系统资源已基本用满,但还能应对。
> CPU 核心数:系统已过载,有进程在排队等待资源。
可使用 lscpu 命令确定系统上的CPU数。
lscpu | grep CPU
1.2 free、buff/cache的区别
在Linux内存管理中,free、buffer 和 cache 是 /proc/meminfo 中的关键指标,共同描述了物理内存的分配与使用状态。与Linux “物尽其用” 的内存管理策略有关,宁可把内存用作缓存来加速系统,也绝不让它闲置。
free 是完全没有被使用的空闲内存。buffers 和 cached 都属于已被使用的内存,但它们是为了提升系统性能而存在的,在应用程序需要时可以被立即回收。所以,通常被合称为 buff/cache。
在评估系统真实内存压力时,不应该只看 free 内存的多少。更科学的做法是结合 available 指标。
available 内存估算的是在不发生Swap的情况下,可供应用程序使用的内存量,available ≈ free + buffer + cache。
因此,一个健康的、性能优化的Linux系统,通常是 free 内存较少,而 buff/cache 占用较多。只有当 available 内存也很少,并且开始使用 swap 时,才说明系统真的面临内存压力。
1.2.1 free - 空闲内存
系统当前完全未被任何进程或内核占用的物理内存。它不承担任何任务,是立即可用的内存资源。
注意:free 内存少不意味着系统内存不足。实际上,在Linux系统中,free 内存越少,往往说明内存利用效率越高,因为内核会尽量利用空闲内存来做 buffers 和 cached 以提升性能。
1.2.2 buffer - 缓冲区
Buffer(缓冲区)是内核在物理内存(RAM)中开辟出来的一块特定区域,专门用于存储文件系统元数据和暂存待写入磁盘的"脏数据",以解决不同设备或组件之间的速度不匹配问题,使写入操作更高效。
比如,当我们要写入数据到磁盘时,数据会先被放到 buffers 内存中,然后由内核在后台统一、批量地写入磁盘。这样,应用程序就不需要等待慢速的磁盘写操作完成,可以立即返回,从而提高了写入效率。
1.2.3 cache - 缓存
cache(缓存)是Linux内存管理的性能加速核心,本质是缓存的是磁盘上文件的实际数据页。目的是加速对文件的后续读取访问。
当数据第一次从磁盘读取后,会被保留在Page Cache中。后续的读取请求若命中Cache,则可直接从内存提供数据,避免了一次磁盘I/O,速度提升几个数量级。
综上,free 是纯粹的空闲,buffers 关乎写磁盘效率,cached 关乎读磁盘效率。
1.3 top/htop
使用 top 命令显示系统进程的动态视图。
top
默认输出列有PID(进程ID)、USER(进程所有者用户名)、VIRT(虚拟内存)、RES(常驻内存)、S(进程状态)、TIME(CPU 时间)、COMMAND(进程命令名称)。
htop # 区别在于 htop 更直观
1.3.1 top 命令基本击键操作
这部分自己尝试一下,不用全记,需要时使用 h 查看帮助即可。
击键 | 功能描述 | 应用场景与说明 |
---|---|---|
h | 显示帮助 | 查看所有可用的击键命令列表。 |
q | 退出 top | 立即退出 top 程序。 |
空格 | 立即刷新 | 手动强制刷新显示,而不等待默认刷新间隔。 |
k | 结束进程 | 输入进程 PID,然后发送信号(默认为 SIGTERM,15)结束该进程。 |
1 | 切换 CPU 显示 | 在整体 CPU 使用率和每个逻辑核心的详细使用率之间切换。 |
l | 切换负载显示 | 显示或隐藏顶部的系统平均负载(Load Average)信息行。 |
t | 切换进程/CPU 显示 | 切换顶部 CPU 状态信息行的图形(条形图/文本)显示模式。 |
m | 切换内存显示 | 切换中部内存交换信息行的图形(条形图/文本)显示模式。 |
M | 按内存排序 | 进程列表立即按照 %MEM(内存使用百分比)降序排列。 |
P | 按 CPU 排序(默认排序方式) | 进程列表立即按照 %CPU(CPU 使用百分比)降序排列。 |
N | 按 PID 排序 | 进程列表按照进程 ID 号降序排列。 |
T | 按时间排序 | 进程列表按照累计 CPU 时间降序排列。 |
R | 反向排序 | 切换当前排序顺序(升序/降序)。 |
u | 按用户筛选 | 输入用户名,只显示属于该用户的进程。 |
i | 切换空闲进程 | 隐藏或显示所有 CPU 使用率为 0 的空闲进程,可以简化列表。 |
Z | 切换颜色 | 开启或关闭颜色显示。 |
x | 高亮排序列 | 高亮显示当前排序所依据的列。 |
b | 高亮运行进程 | 高亮显示正在运行(R 状态)的进程。 |
W | 保存配置 | 将当前的窗口配置(如排序、高亮等)写入 ~/.toprc,下次启动时自动加载。 |
Shift + > | 排序列右移 | 更改进程列表的排序依据为右侧的下一列。 |
Shift + < | 排序列左移 | 更改进程列表的排序依据为左侧的上一列。 |
1.3.2 核心指标解释
第1行:系统概要信息
字段 | 解释 |
---|---|
06:20:26 | 当前系统时间。 |
up 2:13 | 系统运行时间。 |
1 user | 登录用户数。 |
load average | 系统平均负载。 |
第2行:任务(进程)信息
字段 | 解释 |
---|---|
total | 进程总数。 |
running | 运行中的进程数。 |
sleeping | 睡眠中的进程数。绝大多数进程都在等待某个事件(如用户输入、网络请求)而被挂起,这是正常状态。 |
stopped | 已停止的进程数。 |
zombie | 僵尸进程数。已终止但未被父进程回收的进程。数量为0是理想状态,表示没有进程资源泄漏。 |
补充:什么是僵尸进程?什么是孤儿进程?以及如何避免?
僵尸进程指子进程已结束,但尚未被父进程回收,此时会成为僵尸进程,使用 ps aux 查看时,状态标志位为“Z”。危害在于会占用系统资源,主要是系统的PID,它是有限的,如果被大量僵尸进程耗尽,将无法创建新进程。
为避免成为僵尸进程,父进程可使用 wait 或 waitpid 回收子进程。或者通过kill父进程,使得僵尸进程成为孤儿进程,解除“Z”状态。
孤儿进程指父进程先于子进程结束,但是这种进程会被系统的第一个进程——init进程(systemd)接管回收,所以是无害的。
第3行:CPU 状态信息
显示了 CPU 时间在各种状态下的分配百分比(所有CPU核心的汇总)。
字段 | 解释 |
---|---|
us | user:运行普通用户空间进程所花费的时间百分比。 |
sy | system:运行内核空间系统进程所花费的时间百分比。 |
ni | nice:运行被调整过优先级(nice值)的用户进程所花费的时间百分比。 |
id | idle:CPU空闲时间百分比。反应CPU负载 |
wa | I/O wait:CPU等待磁盘I/O操作完成的时间百分比。 |
hi | hardware IRQ:处理硬件中断所花费的时间。 |
si | software IRQ:处理软件中断所花费的时间。 |
st | steal:在虚拟化环境中,被宿主机“偷走” 的CPU时间。 |
第4行:物理内存信息
显示了物理内存的使用情况,单位是 MiB。这部分在 1.2 处有详细介绍。
字段 | 解释 |
---|---|
total | 总物理内存。 |
free | 完全空闲的内存。 |
used | 已使用的内存。 |
buff/cache | 用于缓冲和缓存的内存。 |
第5行:交换空间信息
显示了交换空间(虚拟内存)的使用情况。avail Mem同样在 1.2 处有详细介绍。
字段 | 解释 |
---|---|
total | 总交换空间。 |
free | 空闲的交换空间。 |
used | 已使用的交换空间。 |
avail Mem | 可用内存估算。 |
二、定位卡顿问题
首先关于卡断问题,有几个怀疑对象,CPU、内存、磁盘、网络,在使用 top/htop 命令简单定位问题所在之后,可以进行深入探索以及解决问题。
2.1 简单定位
1. 负载评估:load average
看1分钟、5分钟、15分钟的平均负载。如果负载值持续高于 CPU 核心数,说明系统压力很大。
2. CPU 分析:%Cpu(s) 行
看 us (用户态), sy (内核态), wa (I/O 等待), id (空闲)。
CPU 瓶颈:us 或 sy 持续过高。
I/O 瓶颈:wa 值过高,磁盘问题。
3. 内存分析:MiB Mem 行 + MiB Swap 行
看 free (完全空闲), avail (可用内存,包括可回收的缓存)。
如果 avail 内存接近为零,并且 swap 被大量使用 (used 很高)。这时系统会因频繁换页而卡顿。
4. 进程分析:进程列表
看 %CPU, %MEM, COMMAND,在这里直接找出消耗资源最高的进程。
2.2 具体定位
2.2.1 怀疑 CPU 瓶颈
pidstat 1:每秒输出一次每个进程的CPU使用率,比 top 更详细。
vmstat 1:查看系统范围(system)的CPU上下文切换 (cs) 和中断 (in),如果过高,可能是由过多进程调度导致。1是1s,可根据需要调整时间间隔。
2.2.2 怀疑内存瓶颈
vmstat 1:看 si (swap in) 和 so (swap out) 列。如果它们持续大于0,说明正在发生换页,这是内存不足的证明,会直接导致卡顿。
dmesg 检查内核日志,有无 OOM(内存溢出)或硬件错误
dmesg
2.2.3 怀疑磁盘 I/O 瓶颈
df -h:查看磁盘使用情况,避免因为磁盘满导致的问题。
iostat -xz 1:看 %util:设备利用率,接近100%表示设备饱和。看 await:I/O 请求的平均等待时间(毫秒)。如果这个值很高(如 >10ms),说明磁盘响应慢。看 r/s, w/s:每秒的读写请求数。
-x:显示扩展的设备统计信息。-z:省略在采样期间内没有活动的设备,让输出更简洁,只显示正在工作的磁盘,便于查看。
iotop:用于显示每个进程的磁盘I/O使用情况,直接找到I/O占用率高的进程。(需要sudo权限)
2.2.4 怀疑网络瓶颈
如果服务器本体的资源排查都没有发现明显瓶颈,但依然感觉“卡顿”,那么我会怀疑是网络问题。
netstat:查看当前连接数。
sudo netstat -tunlp | grep -E "(ESTABLISHED|SYN_RECV)" | wc -l
参数 | 全称 | 作用描述 |
---|---|---|
-t | TCP | 仅显示 TCP 协议 相关的连接。 |
-u | UDP | 仅显示 UDP 协议 相关的连接。 |
-n | Numeric | 以数字形式显示地址和端口号,不进行解析。 |
-l | Listen | 仅显示处于 LISTEN(监听) 状态的套接字。 |
-p | Program | 显示占用该套接字的进程ID (PID) 和程序名。(需要sudo权限) |
ss -s:查看详细的Socket统计信息。orphaned: 0 - 孤儿连接。
使用以上两个命令查看是否有大量的网络连接堆积,或者连接状态异常(如大量的 TIME_WAIT 或 CLOSE_WAIT)。
使用 ping 检测到目标服务的网络延迟和丢包率,以及使用 traceroute 追踪网络路径,看延迟发生在哪个环节。
2.3 解决问题
瓶颈类型 | 解决方案 | 核心思路与说明 |
---|---|---|
CPU 瓶颈 | 1. 优化应用代码 | 从根本上减少计算量,消除性能热点。 |
2. 调整进程调度优先级 | 为关键任务分配更多CPU时间片。 | |
3. 水平扩容:增加更多服务器或Pod(K8s) | 通过增加计算单元来分担负载,是云原生时代的首选方案。 | |
4. 升级更快的CPU | 垂直扩容,提升单核处理能力。 | |
内存瓶颈 | 1. 优化应用代码,减少内存泄漏和不合理使用 | 使用内存分析工具定位问题,避免内存浪费。 |
2. 扩容:增加物理内存或配置更大的Swap | Swap是治标不治本,可能引入磁盘I/O延迟。 | |
3. 调整系统的 swappiness 参数 (vm.swappiness) | 控制内核使用Swap的倾向性(0-100),值越低越倾向于保留物理内存。 | |
4. 重启内存泄漏的进程或服务 | 快速恢复服务的临时应急方案。 | |
磁盘 I/O 瓶颈 | 1. 优化查询:例如为数据库添加合适的索引 | 减少不必要的数据扫描和读写量。 |
2. 使用更快的存储:用SSD替换HDD | 从根本上降低I/O延迟,提升吞吐量。 | |
3. 调整文件系统挂载参数 | 减少元数据更新,减少不必要的写操作。 | |
4. 使用缓存(如Redis)来减少对磁盘的直接访问 | 用内存换I/O,将频繁读取的数据放在内存中。 | |
网络瓶颈 | 1. 优化数据传输(如启用压缩) | 减少需要传输的数据量。 |
2. 升级网络带宽 | 直接提升数据传输的管道容量。 | |
3. 使用CDN或更近的机房 | 缩短数据传输的物理距离,降低延迟。 |
如有问题或建议,欢迎在评论区中留言~