回到首页

输入关键词,多个关键词用空格分隔。

← 命令行与工程工具 · 系列目录

Linux 服务器检查指南

约 18 分钟更新于 2026-11-06
本页目录

服务器有时候像一个爱哭爱闹的小宝宝,能听到它在哭,但如果不够熟悉的话,一时半会儿还真不知道到底是为啥而哭。本文结合自己工作中的一些经验,给出一些检查服务器运行状况的通用技巧。


注,章节名称源自英雄联盟中盲僧李青的技能。

更新历史:

  • 2016.09.28: 完成初稿

天音波/回音击

htop

最基本的招式就是 htop 了,基本囊括了各种信息(一般系统默认是 top)

文章配图

观察一段时间大概能对服务器的基本状态有一个认知,可以比较直观找出占用 CPU 和内存的应用。这里简单说明一下如何利用这些数据。

右上角的信息也可以通过 uptime 进行查看,比如:

wdxtub:~$ uptime
06:49:47 up 49 days, 2:29, 1 user, load average: 0.21, 0.13, 0.19

最后的三个数字表示 1 分钟、5 分钟和 15 分钟的平均负载,Linux 的负载包含可运行任务和不可中断睡眠任务,不能直接当成 CPU 使用率。应结合 CPU 核数、持续时间以及 CPU/I/O 指标判断,没有通用的 0.7 警戒线。

free

我们可以使用 free -m 来以 mb 为单位查看内存使用情况,如:

wdxtub:~$ free -m
             total used free shared buffers cached
Mem: 16047 15871 176 0 217 9436
-/+ buffers/cache: 6217 9830
Swap: 0 0 0

这是旧版 free 的输出。free 很少不等于内存不足,缓存中的部分内存可以回收;较新版本应结合 available 看可用量。Swap 已用不必为零,持续的换入换出、应用延迟和内存压力更值得关注。

金钟罩/铁布衫

dmesg

dmesg 查看内核环形缓冲区,关注 OOM、设备错误等线索;它不是全部系统日志,有的系统需要权限才能读取。

vmstat

vmstat 1 每行会输出一些系统核心指标(后面的数字表示每隔 1 秒输出一次)

wdxtub:~$ vmstat 1
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r b swpd free buff cache si so bi bo in cs us sy id wa st
 1 0 0 341460 220212 9530820 0 0 10 436 1 1 8 1 86 5 0
 0 0 0 341452 220212 9530820 0 0 0 10412 580 681 0 0 99 0 0
 0 0 0 341420 220212 9530820 0 0 0 0 371 693 0 0 100 0 0
 0 0 0 341420 220212 9530820 0 0 0 0 389 737 0 0 100 0 0
 1 0 0 339320 220212 9532844 0 0 0 9020 9561 8007 15 4 70 11 0
 0 0 0 335616 220212 9536784 0 0 0 0 1149 1295 6 1 93 0 0
 0 0 0 339984 220212 9532188 0 0 0 5428 581 832 0 0 100 0 0

具体介绍一下几个特别需要关注的指标:

  • r 值,可运行任务数,包括正在运行和等待 CPU 的任务;持续超过逻辑 CPU 数量时,需要结合利用率检查 CPU 竞争
  • free 值,当前空闲内存大小(单位: KB)
  • si 与 so 值,分别表示从交换区换入与换出的速率;持续较高时应检查内存压力,不能凭一次非零采样直接判定故障
  • us, sy, id, wa, st 值,表示用户时间(user),系统时间(system),空闲时间(idle),IO 等待时间(wait)和被偷走的时间(stolen,一般被其他虚拟机消耗)

如果用户时间和系统时间相加比较大,则 CPU 忙于执行指令,IO 等待较高时,应进一步结合设备延迟、吞吐和应用行为确认瓶颈。vmstat 的首行通常报告自启动以来的平均值,后续行才对应采样间隔。

pidstat

需要先安装 sudo apt install sysstat,然后使用 pidstat 1 可以查看进程的 CPU 占用率

wdxtub:~$ pidstat 3
Linux 3.13.0-91-generic 09/26/2016 _x86_64_ (4 CPU)

07:16:38 AM UID PID %usr %system %guest %CPU CPU Command
07:16:41 AM 0 7 0.00 0.33 0.00 0.33 2 rcu_sched

07:16:41 AM UID PID %usr %system %guest %CPU CPU Command
07:16:44 AM 1001 20573 0.00 0.33 0.00 0.33 2 pidstat

07:16:44 AM UID PID %usr %system %guest %CPU CPU Command
07:16:47 AM 0 10 0.00 0.33 0.00 0.33 1 rcuos/2
07:16:47 AM 0 386 0.00 0.33 0.00 0.33 1 jbd2/dm-0-8
07:16:47 AM 1001 13003 0.00 0.67 0.00 0.67 0 nginx
07:16:47 AM 1001 13004 0.00 0.67 0.00 0.67 1 nginx
07:16:47 AM 1001 13005 0.67 0.67 0.00 1.33 2 nginx
07:16:47 AM 1001 13006 0.67 0.00 0.00 0.67 3 nginx
07:16:47 AM 1001 22179 0.33 0.00 0.00 0.33 1 node

07:16:47 AM UID PID %usr %system %guest %CPU CPU Command
07:16:50 AM 1001 19021 0.33 0.33 0.00 0.67 1 sshd
07:16:50 AM 1001 20573 0.00 0.33 0.00 0.33 2 pidstat

07:16:50 AM UID PID %usr %system %guest %CPU CPU Command
07:16:53 AM 0 8 0.00 0.33 0.00 0.33 2 rcuos/0
07:16:53 AM 0 386 0.00 0.33 0.00 0.33 1 jbd2/dm-0-8
07:16:53 AM 1001 3408 76.33 8.33 0.00 84.67 3 java
07:16:53 AM 1001 13003 0.00 0.67 0.00 0.67 0 nginx
07:16:53 AM 1001 13005 0.67 0.67 0.00 1.33 2 nginx
07:16:53 AM 1001 13006 0.33 0.33 0.00 0.67 3 nginx
07:16:53 AM 1001 22179 0.00 0.33 0.00 0.33 0 node

07:16:53 AM UID PID %usr %system %guest %CPU CPU Command
07:16:56 AM 1001 3408 1.00 0.00 0.00 1.00 3 java
07:16:56 AM 1001 20573 0.00 0.33 0.00 0.33 2 pidstat
^C

Average: UID PID %usr %system %guest %CPU CPU Command
Average: 0 7 0.00 0.06 0.00 0.06 - rcu_sched
Average: 0 8 0.00 0.06 0.00 0.06 - rcuos/0
Average: 0 10 0.00 0.06 0.00 0.06 - rcuos/2
Average: 0 386 0.00 0.11 0.00 0.11 - jbd2/dm-0-8
Average: 1001 3408 12.89 1.39 0.00 14.28 - java
Average: 1001 13003 0.00 0.22 0.00 0.22 - nginx
Average: 1001 13004 0.00 0.11 0.00 0.11 - nginx
Average: 1001 13005 0.22 0.22 0.00 0.44 - nginx
Average: 1001 13006 0.17 0.06 0.00 0.22 - nginx
Average: 1001 19021 0.06 0.06 0.00 0.11 - sshd
Average: 1001 20573 0.00 0.17 0.00 0.17 - pidstat
Average: 1001 22179 0.06 0.06 0.00 0.11 - node

这个命令的好处是不会覆盖之前的数据,我们能够方便地观察系统动态。

iostat

可以使用 iostat -xz 1 来查看机器磁盘 IO 情况,如:

wdxtub:~$ iostat -xz 1
Linux 3.13.0-91-generic 09/26/2016 _x86_64_ (4 CPU)

avg-cpu: %user %nice %system %iowait %steal %idle
           8.01 0.00 0.71 5.04 0.02 86.22

Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
xvda 0.00 0.20 0.07 0.34 1.70 3.60 26.23 0.00 1.43 1.06 1.50 0.24 0.01
xvdb 0.00 158.04 1.76 47.14 28.13 1289.27 53.89 0.16 3.21 3.49 3.20 0.71 3.49
dm-0 0.00 0.00 2.48 228.71 37.46 1732.41 15.31 0.06 0.24 7.13 0.17 0.56 12.89
xvdf 0.00 1.19 0.72 22.34 9.33 443.14 39.24 0.58 24.94 15.99 25.23 4.28 9.87

avg-cpu: %user %nice %system %iowait %steal %idle
           0.00 0.00 0.00 0.00 0.00 100.00

Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
xvdb 0.00 143.00 0.00 6.00 0.00 596.00 198.67 0.00 0.00 0.00 0.00 0.00 0.00
dm-0 0.00 0.00 0.00 149.00 0.00 596.00 8.00 0.00 0.00 0.00 0.00 0.00 0.00

avg-cpu: %user %nice %system %iowait %steal %idle
           0.25 0.00 0.00 0.00 0.00 99.75
  • r/s, w/s, rkB/s, wkB/s 表示每秒的读写次数和每秒读写的数据量,需要结合设备能力和业务基线比较
  • await 是请求完成的平均耗时,包括排队与服务时间;升高不一定意味着硬件故障
  • avgqu-sz 是平均队列长度,较新版本可能显示为 aqu-sz;不能用大于 1 判断所有设备是否饱和
  • %util 反映采样期间设备有 I/O 请求的时间比例;多队列 SSD、RAID 等设备不能仅凭这个值判断性能上限

天雷破/摧筋断骨

sar

sar -n DEV 1 用来查看网络设备的吞吐率,如

wdxtub@demo-host:~$ sar -n DEV 1
Linux 3.13.0-91-generic (demo-host) 09/26/2016 _x86_64_ (4 CPU)

07:49:06 AM IFACE rxpck/s txpck/s rxkB/s txkB/s rxcmp/s txcmp/s rxmcst/s %ifutil
07:49:07 AM eth0 2.00 0.00 0.13 0.00 0.00 0.00 0.00 0.00
07:49:07 AM lo 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00

07:49:10 AM IFACE rxpck/s txpck/s rxkB/s txkB/s rxcmp/s txcmp/s rxmcst/s %ifutil
07:49:11 AM eth0 289.00 214.00 202.80 60.45 0.00 0.00 0.00 0.12
07:49:11 AM lo 206.00 206.00 180.29 180.29 0.00 0.00 0.00 0.00

07:49:15 AM IFACE rxpck/s txpck/s rxkB/s txkB/s rxcmp/s txcmp/s rxmcst/s %ifutil
07:49:16 AM eth0 2364.00 1723.00 1488.95 459.65 0.00 0.00 0.00 0.87
07:49:16 AM lo 1771.00 1771.00 1101.98 1101.98 0.00 0.00 0.00 0.00

Average: IFACE rxpck/s txpck/s rxkB/s txkB/s rxcmp/s txcmp/s rxmcst/s %ifutil
Average: eth0 275.37 204.90 176.24 93.42 0.00 0.00 0.00 0.10
Average: lo 204.82 204.82 137.94 137.94 0.00 0.00 0.00 0.00

可以看到网络流量的波动特别大,但是远没有到达瓶颈,所以可能是其他的问题,需要继续排查。

sar -n TCP,ETCP 1 用来查看 TCP 连接的状态,如

wdxtub@demo-host:~$ sar -n TCP,ETCP 1
Linux 3.13.0-91-generic (demo-host) 09/26/2016 _x86_64_ (4 CPU)

07:52:50 AM active/s passive/s iseg/s oseg/s
07:52:51 AM 0.00 0.00 2.00 0.00

07:52:50 AM atmptf/s estres/s retrans/s isegerr/s orsts/s
07:52:51 AM 0.00 0.00 0.00 0.00 0.00

07:52:54 AM active/s passive/s iseg/s oseg/s
07:52:55 AM 176.00 339.00 4046.00 3443.00

07:52:54 AM atmptf/s estres/s retrans/s isegerr/s orsts/s
07:52:55 AM 0.00 0.00 0.00 0.00 0.00

07:52:57 AM active/s passive/s iseg/s oseg/s
07:52:58 AM 4.00 4.00 32.00 32.00

07:52:57 AM atmptf/s estres/s retrans/s isegerr/s orsts/s
07:52:58 AM 0.00 0.00 0.00 0.00 0.00

Average: active/s passive/s iseg/s oseg/s
Average: 21.48 41.25 495.45 422.05

Average: atmptf/s estres/s retrans/s isegerr/s orsts/s
Average: 0.00 0.00 0.00 0.00 0.00

其中:

  • active/s 是每秒本地发起的 TCP 连接数目,即 connect
  • passive/s 是每秒远程发起的 TCP 连接数目,即 accept
  • retrans/s 每秒 TCP 重传数量

猛龙摆尾

虽然现在各种框架和服务器基本已经帮我们处理好了 JVM 相关事宜,不过有些时候出了比较奇怪的问题,还是需要深入到 JVM 内部才能找到问题根源(最近我就经历了这样一次排查),下面是相关的一些命令指南。

jps

输出 JVM 中运行的进程状态信息,比如 jps -m -l,会输出传入 main 方法的参数和类的全名,如:

wdxtub@:~$ jps -m -l
21843 org.elasticsearch.bootstrap.Elasticsearch start
23012 sun.tools.jps.Jps -m -l

jstack

用来查看某个 Java 进程内的线程堆栈信息。-l 可输出额外锁信息;具体选项依 JDK 版本而定,旧版的 -m 不能直接照搬。

我们可以 htop 查看进程信息,随手找一个 Java 应用,比方说 elasticsearch,对应的 pid 是 32346(不同机器不一样)。当然,也可以使用命令 ps -ef | grep elasticsearch | grep -v grep。

然后我们可以看看这个进程内最吃 CPU 的线程,可以用的命令比较多,比如:ps -Lfp 32346, ps -mp 32346 -o THREAD,tid,time | sort 或 top -Hp 32346

我们这里使用 ps -mp 32346 -o THREAD,tid,time | sort,排序之后的结果是

wdxtub 0.4 19 - futex_ - - 571 00:05:31
wdxtub 0.6 19 - futex_ - - 32374 00:08:57
wdxtub 0.7 19 - futex_ - - 32452 00:10:48
wdxtub 0.7 19 - futex_ - - 32642 00:11:01
wdxtub 0.8 19 - futex_ - - 18940 00:07:26
wdxtub 0.8 19 - futex_ - - 32412 00:11:27
wdxtub 0.8 19 - futex_ - - 471 00:11:31
wdxtub 0.8 19 - futex_ - - 472 00:11:15
wdxtub 0.9 19 - futex_ - - 32650 00:13:03
wdxtub 28.5 - - - - - - 06:36:47
USER %CPU PRI SCNT WCHAN USER SYSTEM TID TIME

我们这里记录下最耗费时间的线程,编号为 32650,转换成十六进制为 0x7f8a,然后在完整线程转储中定位对应的原生线程 ID,

wdxtub:~$ jstack 32346 | grep 7f8a
"elasticsearch[node-2][refresh][T#2]" #279 daemon prio=5 os_prio=0 tid=0x00007fd39447a000 nid=0x7f8a waiting on condition [0x00007fd147d2c000]

这一行只能说明采样时线程在等待某个条件。需要查看后续完整堆栈,并多次采样对照,不能凭一行线程标题判断根因。累计 CPU 时间也不等同于刚才采样区间的消耗。

堆信息与历史 jmap 示例

下面保留 JDK 8 的 jmap 输出,用于学习字段含义,旧版选项不保证适用于其他 JDK。可以先用 jcmd pid help 查看目标 JVM 支持的诊断命令,再选择 GC.heap_info、Thread.print 等命令。

jmap -heap pid 查看堆内存使用情况,包括使用的GC算法、堆配置参数和各代中堆内存使用情况,一个例子如下:

wdxtub:~$ jmap -heap 8183
Attaching to process ID 8183, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.102-b14

using thread-local object allocation.
Parallel GC with 4 thread(s)

Heap Configuration:
   MinHeapFreeRatio = 0
   MaxHeapFreeRatio = 100
   MaxHeapSize = 1073741824 (1024.0MB)
   NewSize = 357564416 (341.0MB)
   MaxNewSize = 357564416 (341.0MB)
   OldSize = 716177408 (683.0MB)
   NewRatio = 2
   SurvivorRatio = 8
   MetaspaceSize = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize = 17592186044415 MB
   G1HeapRegionSize = 0 (0.0MB)

Heap Usage:
PS Young Generation
Eden Space:
   capacity = 335544320 (320.0MB)
   used = 334734664 (319.2278518676758MB)
   free = 809656 (0.7721481323242188MB)
   99.75870370864868% used
From Space:
   capacity = 11010048 (10.5MB)
   used = 8929984 (8.51629638671875MB)
   free = 2080064 (1.98370361328125MB)
   81.10758463541667% used
To Space:
   capacity = 11010048 (10.5MB)
   used = 0 (0.0MB)
   free = 11010048 (10.5MB)
   0.0% used
PS Old Generation
   capacity = 716177408 (683.0MB)
   used = 439459664 (419.1013946533203MB)
   free = 276717744 (263.8986053466797MB)
   61.361844019519815% used

20959 interned Strings occupying 2601056 bytes.

使用 jmap -histo[:live] pid 查看堆内存中的对象数目、大小统计直方图,如果带上 live 则只统计活对象,如下:

wdxtub:~$ jmap -histo:live 8183
 num #instances #bytes class name
----------------------------------------------
   1: 25679 47998512 [B
   2: 66216 15910168 [C
   3: 8707 2980024 [I
   4: 62197 1492728 java.lang.String
   5: 28749 919968 java.util.HashMap$Node
   6: 17168 824064 org.apache.tomcat.util.buf.ByteChunk
   7: 15363 737424 org.apache.tomcat.util.buf.CharChunk
   8: 14563 699024 org.apache.tomcat.util.buf.MessageBytes
   9: 6009 677840 java.lang.Class
  10: 9152 608784 [Ljava.lang.Object;
  11: 1976 497128 [Ljava.util.HashMap$Node;
  12: 15396 492672 java.util.concurrent.ConcurrentHashMap$Node
  13: 6690 380824 [Ljava.lang.String;
  14: 4270 375760 java.lang.reflect.Method
  15: 448 206128 [Ljava.util.concurrent.ConcurrentHashMap$Node;
  16: 11699 187184 java.lang.Object
  17: 2687 171968 java.net.URL
  18: 2887 138576 java.util.HashMap
  19: 4154 132928 java.util.Hashtable$Entry
  20: 5077 121848 org.apache.tomcat.util.http.MimeHeaderField
  21: 2923 116920 java.util.LinkedHashMap$Entry
  22: 2492 99680 org.apache.catalina.loader.ResourceEntry
  23: 678 97632 java.text.DecimalFormat
  24: 1420 90880 java.util.concurrent.ConcurrentHashMap
  25: 3703 88872 java.util.ArrayList
  26: 1795 86160 java.nio.HeapByteBuffer
  27: 2017 80680 java.lang.ref.SoftReference
  28: 691 77392 java.util.GregorianCalendar
  29: 1372 76832 java.util.LinkedHashMap
  30: 2246 71872 java.lang.ref.WeakReference

其中 class name 有一些缩写,具体的意思是:

B byte
C char
D double
F float
I int
J long
Z boolean
[ 数组,如[I表示int[]
[L+类名 其他对象

堆转储和活对象统计可能影响运行中的 JVM,并可能包含业务数据。执行前应了解所用 JDK 的影响说明。原稿使用的 jhat 已在 JDK 9 移除,不能继续按旧教程启动它;参见 JDK 9 迁移指南。

jstat

这个命令主要用来统计 JVM 信息,必须要指定的是虚拟机 ID,一般就是进程号,比方说 jstat -gc 8183 500 5 就是指 500ms 采样一次,采样数字为 5,比如:

wdxtub:~$ jstat -gc 8183 500 5
 S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
10752.0 10752.0 8881.2 0.0 327680.0 2318.5 699392.0 79410.9 36992.0 35946.6 3968.0 3721.2 5567 54.352 6 0.711 55.063
10752.0 10752.0 8881.2 0.0 327680.0 5282.0 699392.0 79410.9 36992.0 35946.6 3968.0 3721.2 5567 54.352 6 0.711 55.063
10752.0 10752.0 8881.2 0.0 327680.0 7929.1 699392.0 79410.9 36992.0 35946.6 3968.0 3721.2 5567 54.352 6 0.711 55.063
10752.0 10752.0 8881.2 0.0 327680.0 9310.1 699392.0 79410.9 36992.0 35946.6 3968.0 3721.2 5567 54.352 6 0.711 55.063
10752.0 10752.0 8881.2 0.0 327680.0 11648.8 699392.0 79410.9 36992.0 35946.6 3968.0 3721.2 5567 54.352 6 0.711 55.063

这里需要先讲解一下 JVM 堆内存的设计

文章配图

上图是包含永久代的旧示意图。JDK 8 已使用 Metaspace 保存类元数据,不能把永久代当成当前堆的一部分。示例使用 Parallel GC;其他收集器的布局、回收阶段和停顿行为可能不同。判断 GC 问题应观察日志、分配速率和停顿分布,不按“某代必然使用某算法”推断。

现在可以来介绍前面各个字段的意思了:

  • S0C、S1C、S0U、S1U:Survivor 0/1 区容量(Capacity)和使用量(Used)
  • EC、EU:Eden 区容量和使用量
  • OC、OU:年老代容量和使用量
  • MC、MU:Metaspace 容量和使用量
  • CCSC、CCSU:压缩类空间容量和使用量
  • YGC、YGCT:年轻代 GC 次数和 GC 耗时
  • FGC、FGCT:Full GC 次数和 Full GC 耗时
  • GCT:GC 总耗时

总结

工作以来除了开发工作,也承担了一部分运维的责任,所以 DevOps 最大的推动力其实就是『不够人』。服务器出现异常,一般来说是由于多种因素共同作用引起的,这就需要我们能够以全局的视角,顺藤摸瓜,各个击破,找到问题最终的根源,才能治标治本。

这些笔记最重要的是排查顺序:先确认影响与时间范围,再用多个指标相互验证,最后回到具体应用。

参考链接

I/O 字段说明参见 sysstat 的 iostat 手册。

相关文章