在服务器整机上部署的应用出现性能问题时,可先在服务器层面尝试调优。 本文记录了服务器硬件信息查询方法、性能指标的常用监测方法以及与性能相关的服务器配置等。

通用资源监控

nmon 是一个通用的系统资源监控工具,执行 nmon 后会显示命令帮助:

  • c:CPU。
  • m:内存。
  • d:硬盘。
    • Busy(使用率):磁盘 I/O 请求的饱和程度。
    • Read:读吞吐量。
    • Write:写吞吐量。
  • n:网络。

主板

# 查询主板型号
dmidecode -s system-product-name

CPU

硬件信息查询:

# 查询 CPU 信息:架构、型号、线程、核、路、频率等
lscpu

# NUMA 架构的 CPU 与内存分布
numactl -H

htop

htop 是一个系统监控与进程管理软件,能提供比 top 更详细的信息。 NUMA 架构设备通常包含几十甚至上百个 CPU 核心,如果需要分别监控它们的负载,使用 htop 会非常方便。

执行 htop 之后按 F2 可以进入交互式的配置菜单;也可以直接修改配置文件 ${HOME}/.config/htop/htoprc,这里提供一个简单配置:

header_layout=two_50_50
show_cpu_usage=1
show_cpu_frequency=1
column_meters_0=LeftCPUs4 Memory Tasks Uptime
column_meter_modes_0=1 1 2 2
column_meters_1=RightCPUs4 Swap LoadAverage CPU
column_meter_modes_1=1 1 2 2

这套配置的效果:

  • 排版是左右两列各占 50% 宽度。
  • CPU 信息包含 CPU 使用率与频率。
  • 左列自上往下分别为:
    • CPU 信息,每行 4 个;条形图。
    • 内存占用;条形图。
    • 任务信息(进程数、线程数、on-CPU 的线程数);文本。
    • 正常运行时间;文本。
  • 右列自上往下分别为:
    • CPU 信息,每行 4 个;条形图。
    • 交换分区占用;条形图。
    • 一段时间内 CPU 的平均负载(1 分钟、5 分钟、 15 分钟);文本。
    • 总体的 CPU 平均使用率;文本。

输出大概是这样:

    0[||||||||||||||61.0% 2895MHz]  32[||||||||||||||90.7%|2898MHz]  64[||||||||||||||84.1%|2897MHz]  96[||||||||||||||93.3%|2897MHz] 128[|||||||||||||||87.2%|2900MHz]160[|||||||||||||||91.1%|2900MHz]192[|||||||||||||||93.5%|2900MHz]224[|||||||||||||||88.9%|2900MHz]
    1[||||||||||||||90.9%|2895MHz]  33[||||||||||||||91.1%|2898MHz]  65[||||||||||||||86.0%|2897MHz]  97[||||||||||||||88.4%|2897MHz] 129[|||||||||||||||86.7%|2900MHz]161[|||||||||||||||84.1%|2900MHz]193[|||||||||||||||91.3%|2900MHz]225[|||||||||||||||90.9%|2900MHz]
    2[||||||||||||||88.9%|2895MHz]  34[||||||||||||||89.1%|2894MHz]  66[||||||||||||||88.6%|2896MHz]  98[||||||||||||||88.9%|2896MHz] 130[|||||||||||||||87.0%|2900MHz]162[|||||||||||||||84.4%|2900MHz]194[|||||||||||||||93.2%|2900MHz]226[|||||||||||||||89.1%|2900MHz]
    3[||||||||||||||93.5%|2895MHz]  35[||||||||||||||91.3%|2894MHz]  67[||||||||||||||84.1%|2896MHz]  99[||||||||||||||89.1%|2896MHz] 131[|||||||||||||||91.1%|2900MHz]163[|||||||||||||||83.7%|2900MHz]195[|||||||||||||||90.9%|2900MHz]227[|||||||||||||||88.9%|2900MHz]
    4[||||||||||||||87.5%|2899MHz]  36[||||||||||||||87.5%|2902MHz]  68[||||||||||||||88.9%|2896MHz] 100[||||||||||||||90.9%|2908MHz] 132[|||||||||||||||87.0%|2900MHz]164[|||||||||||||||93.6%|2900MHz]196[|||||||||||||||89.1%|2900MHz]228[|||||||||||||||90.7%|2900MHz]
    5[||||||||||||||91.1%|2899MHz]  37[||||||||||||||93.3%|2902MHz]  69[||||||||||||||86.4%|2896MHz] 101[||||||||||||||88.9%|2908MHz] 133[|||||||||||||||88.9%|2900MHz]165[|||||||||||||||89.6%|2900MHz]197[|||||||||||||||89.6%|2900MHz]229[|||||||||||||||83.7%|2900MHz]
    6[||||||||||||||86.7%|2902MHz]  38[||||||||||||||89.1%|2897MHz]  70[||||||||||||||87.0%|2895MHz] 102[||||||||||||||91.1%|2904MHz] 134[|||||||||||||||89.4%|2900MHz]166[|||||||||||||||89.1%|2900MHz]198[|||||||||||||||88.9%|2900MHz]230[|||||||||||||||91.5%|2900MHz]
    7[||||||||||||||90.5%|2902MHz]  39[||||||||||||||86.4%|2897MHz]  71[||||||||||||||88.6%|2895MHz] 103[||||||||||||||88.4%|2904MHz] 135[|||||||||||||||88.6%|2900MHz]167[|||||||||||||||84.8%|2900MHz]199[|||||||||||||||91.3%|2900MHz]231[|||||||||||||||89.4%|2900MHz]
    8[||||||||||||||88.9%|2902MHz]  40[||||||||||||||87.2%|2899MHz]  72[||||||||||||||88.6%|2898MHz] 104[||||||||||||||87.2%|2900MHz] 136[|||||||||||||||81.8%|2899MHz]168[|||||||||||||||87.0%|2900MHz]200[|||||||||||||||91.3%|2900MHz]232[|||||||||||||||90.9%|2900MHz]
    9[||||||||||||||86.4%|2902MHz]  41[||||||||||||||88.6%|2899MHz]  73[||||||||||||||87.0%|2898MHz] 105[||||||||||||||88.9%|2900MHz] 137[|||||||||||||||91.5%|2899MHz]169[|||||||||||||||91.1%|2900MHz]201[|||||||||||||||93.6%|2900MHz]233[|||||||||||||||88.6%|2900MHz]
   10[||||||||||||||87.0%|2903MHz]  42[||||||||||||||93.3%|2901MHz]  74[||||||||||||||84.8%|2900MHz] 106[||||||||||||||84.1%|2905MHz] 138[|||||||||||||||91.3%|2900MHz]170[|||||||||||||||91.3%|2900MHz]202[|||||||||||||||91.3%|2900MHz]234[|||||||||||||||91.3%|2900MHz]
   11[||||||||||||||88.9%|2903MHz]  43[||||||||||||||87.0%|2901MHz]  75[||||||||||||||91.1%|2900MHz] 107[||||||||||||||90.9%|2905MHz] 139[|||||||||||||||87.2%|2900MHz]171[|||||||||||||||87.2%|2900MHz]203[|||||||||||||||90.7%|2900MHz]235[|||||||||||||||90.7%|2900MHz]
   12[||||||||||||||86.4%|2900MHz]  44[||||||||||||||89.1%|2904MHz]  76[||||||||||||||89.4%|2897MHz] 108[||||||||||||||86.7%|2899MHz] 140[|||||||||||||||86.7%|2900MHz]172[|||||||||||||||91.1%|2900MHz]204[|||||||||||||||95.7%|2900MHz]236[|||||||||||||||89.4%|2900MHz]
   13[||||||||||||||91.3%|2900MHz]  45[||||||||||||||89.1%|2904MHz]  77[||||||||||||||88.6%|2897MHz] 109[||||||||||||||89.1%|2899MHz] 141[|||||||||||||||89.6%|2900MHz]173[|||||||||||||||91.1%|2900MHz]205[|||||||||||||||93.2%|2900MHz]237[|||||||||||||||88.9%|2900MHz]
   14[||||||||||||||88.1%|2899MHz]  46[||||||||||||||88.9%|2896MHz]  78[||||||||||||||90.9%|2900MHz] 110[||||||||||||||87.0%|2900MHz] 142[|||||||||||||||91.3%|2900MHz]174[|||||||||||||||87.0%|2900MHz]206[|||||||||||||||91.3%|2900MHz]238[|||||||||||||||86.7%|2900MHz]
   15[||||||||||||||93.3%|2899MHz]  47[||||||||||||||93.5%|2896MHz]  79[||||||||||||||88.9%|2900MHz] 111[||||||||||||||90.9%|2900MHz] 143[|||||||||||||||89.1%|2900MHz]175[|||||||||||||||89.4%|2900MHz]207[|||||||||||||||91.7%|2900MHz]239[|||||||||||||||93.5%|2900MHz]
   16[||||||||||||||90.7%|2896MHz]  48[||||||||||||||91.3%|2904MHz]  80[||||||||||||||88.4%|2892MHz] 112[||||||||||||||88.9%|2898MHz] 144[|||||||||||||||87.5%|2900MHz]176[|||||||||||||||91.5%|2900MHz]208[|||||||||||||||84.4%|2900MHz]240[|||||||||||||||93.5%|2900MHz]
   17[||||||||||||||93.2%|2896MHz]  49[||||||||||||||90.7%|2904MHz]  81[||||||||||||||93.0%|2892MHz] 113[||||||||||||||88.6%|2898MHz] 145[|||||||||||||||88.4%|2900MHz]177[|||||||||||||||88.6%|2900MHz]209[|||||||||||||||89.1%|2900MHz]241[|||||||||||||||93.2%|2900MHz]
   18[||||||||||||||86.7%|2896MHz]  50[||||||||||||||87.0%|2895MHz]  82[||||||||||||||80.4%|2902MHz] 114[||||||||||||||88.4%|2900MHz] 146[|||||||||||||||87.0%|2900MHz]178[|||||||||||||||86.7%|2900MHz]210[|||||||||||||||93.3%|2900MHz]242[|||||||||||||||88.6%|2900MHz]
   19[||||||||||||||87.0%|2896MHz]  51[||||||||||||||89.4%|2895MHz]  83[||||||||||||||91.1%|2902MHz] 115[||||||||||||||84.1%|2900MHz] 147[|||||||||||||||91.3%|2900MHz]179[|||||||||||||||83.7%|2900MHz]211[|||||||||||||||95.7%|2900MHz]243[|||||||||||||||88.6%|2900MHz]
   20[||||||||||||||93.5%|2902MHz]  52[||||||||||||||91.1%|2894MHz]  84[||||||||||||||85.1%|2901MHz] 116[||||||||||||||91.5%|2902MHz] 148[|||||||||||||||89.4%|2900MHz]180[|||||||||||||||89.1%|2900MHz]212[|||||||||||||||93.8%|2900MHz]244[|||||||||||||||89.1%|2900MHz]
   21[||||||||||||||91.3%|2902MHz]  53[||||||||||||||89.8%|2894MHz]  85[||||||||||||||88.9%|2901MHz] 117[||||||||||||||86.7%|2902MHz] 149[|||||||||||||||86.4%|2900MHz]181[|||||||||||||||88.6%|2900MHz]213[|||||||||||||||88.6%|2900MHz]245[|||||||||||||||87.2%|2900MHz]
   22[||||||||||||||84.1%|2898MHz]  54[||||||||||||||89.1%|2900MHz]  86[||||||||||||||89.4%|2906MHz] 118[||||||||||||||91.3%|2905MHz] 150[|||||||||||||||88.9%|2900MHz]182[|||||||||||||||91.3%|2900MHz]214[|||||||||||||||86.4%|2900MHz]246[|||||||||||||||91.5%|2900MHz]
   23[||||||||||||||86.7%|2898MHz]  55[||||||||||||||81.4%|2900MHz]  87[||||||||||||||91.3%|2906MHz] 119[||||||||||||||89.4%|2905MHz] 151[|||||||||||||||90.9%|2900MHz]183[|||||||||||||||89.8%|2900MHz]215[|||||||||||||||88.6%|2900MHz]247[|||||||||||||||91.3%|2900MHz]
   24[||||||||||||||91.1%|2896MHz]  56[||||||||||||||89.4%|2904MHz]  88[||||||||||||||90.9%|2900MHz] 120[||||||||||||||85.7%|2901MHz] 152[|||||||||||||||88.6%|2900MHz]184[|||||||||||||||88.6%|2900MHz]216[|||||||||||||||86.4%|2900MHz]248[|||||||||||||||87.2%|2900MHz]
   25[||||||||||||||91.3%|2896MHz]  57[||||||||||||||91.1%|2904MHz]  89[||||||||||||||91.5%|2900MHz] 121[||||||||||||||89.4%|2901MHz] 153[|||||||||||||||83.7%|2900MHz]185[|||||||||||||||88.9%|2900MHz]217[|||||||||||||||91.3%|2900MHz]249[|||||||||||||||90.9%|2900MHz]
   26[||||||||||||||88.6%|2904MHz]  58[||||||||||||||80.0%|2904MHz]  90[||||||||||||||89.1%|2899MHz] 122[||||||||||||||82.6%|2907MHz] 154[|||||||||||||||89.8%|2900MHz]186[|||||||||||||||77.8%|2900MHz]218[|||||||||||||||87.2%|2900MHz]250[|||||||||||||||75.6%|2900MHz]
   27[||||||||||||||87.0%|2904MHz]  59[||||||||||||||78.3%|2904MHz]  91[||||||||||||||84.4%|2899MHz] 123[||||||||||||||68.9% 2907MHz] 155[|||||||||||||||86.7%|2900MHz]187[|||||||||||||||75.0%|2900MHz]219[|||||||||||||||88.9%|2900MHz]251[|||||||||||||||88.9%|2900MHz]
   28[||||||||||||||90.9%|2900MHz]  60[||||||||||||||79.1%|2903MHz]  92[||||||||||||||84.1%|2899MHz] 124[||||||||||||||79.5%|2896MHz] 156[|||||||||||||||88.9%|2900MHz]188[|||||||||||||||77.8%|2900MHz]220[|||||||||||||||93.5%|2900MHz]252[|||||||||||||||91.3%|2900MHz]
   29[||||||||||||||93.3%|2900MHz]  61[||||||||||||||82.2%|2903MHz]  93[||||||||||||||89.6%|2899MHz] 125[||||||||||||||75.6%|2896MHz] 157[|||||||||||||||91.7%|2900MHz]189[|||||||||||||||73.3%|2900MHz]221[|||||||||||||||86.7%|2900MHz]253[|||||||||||||||75.0%|2900MHz]
   30[||||||||||||||88.6%|2901MHz]  62[||||||||||||||75.6%|2904MHz]  94[||||||||||||||86.7%|2897MHz] 126[||||||||||||||72.7%|2908MHz] 158[|||||||||||||||89.4%|2900MHz]190[|||||||||||||||82.2%|2900MHz]222[|||||||||||||||85.1%|2900MHz]254[|||||||||||||||80.4%|2900MHz]
   31[||||||||||||||89.1%|2901MHz]  63[||||||||||||||68.9% 2904MHz]  95[||||||||||||||89.6%|2897MHz] 127[||||||||||||||68.2% 2908MHz] 159[|||||||||||||||84.4%|2900MHz]191[|||||||||||||||75.6%|2900MHz]223[|||||||||||||||91.3%|2900MHz]255[|||||||||||||||77.8%|2900MHz]
  Mem[||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||193G/1006G] Swp[||                                                                                                                  12.6M/4.00G]
  Tasks: 40, 1046 thr, 2048 kthr; 0 running                                                                                           Load average: 494.98 353.27 206.93
  Uptime: 03:48:04                                                                                                                    Avg: 63.9% sys: 24.0% low:  0.0% vir:  0.0% freq: 2899MHz

如果希望将 htop 的快照输出到文件中:

# 输出到文件
echo | htop > htop.out

# 打印文件
head -c -10 htop.out | tail -c +10

注意一下,CPU 使用率与 CPU 负载是两种不同的概念。

CPU 使用率是指在一个时间段内 CPU 处于忙碌状态的比例。 在某个时间戳时,CPU 的状态是离散的,只有忙碌和空闲两种状态。 因此统计出的 CPU 使用率不可能超过 100%。 htop 实际是访问文件 /proc/stat 获取 CPU 在不同状态下花费的时间(以 USER_HZ 为单位,通常是 10ms),通过时间段前后的差值计算出的比例作为 CPU 使用率。 /proc/stat 中 CPU 的状态包括:user(用户态)、nice(低优先级用户态)、system(内核态)、idle(空闲)、iowait(I/O 等待)、irq(中断处理)、softirq(软中断处理)等。 htop 是通过不同颜色区分展示这些状态在 CPU 使用率中的占比,支持基础模式(默认)与详细模式(Detailed CPU time)两种显示模式。 比如在默认的基础显示模式中,I/O 等待与 IRQ 处理会合并显示为同一种颜色;如果修改为详细显示模式,它们会被不同的颜色代表。 大部分情况下,我们可以使用默认的基础模式,能够粗略区分用户进程与内核空间代码的占比就行。

而 CPU 负载衡量的不仅仅是测量时的活动量,还包含了线程对 CPU 的需求程度。 在理想情况下,可以认为 CPU 负载测量的是 Linux 运行队列中标记为 runnable(可运行)与 uninterruptible sleep(不可中断睡眠)的线程数的移动平均值。 因此只要符合条件的线程足够多,CPU 负载是能够超过 100% 的。 多线程调度是以时间片为单位将线程分配给 CPU 执行,用户线程在进入系统调用或时间片结束时放弃控制并回到内核态。 这时内核会以固定频率依据运行队列中的线程状态统计 CPU 负载(计算移动平均值)。 有意思的是,内核在计算移动平均值时用了一点魔法,不过可以不用管。 一般情况下认为将 CPU 负载控制在 70% 以下是比较稳定的。 顺带一提,可以通过命令 cat /proc/loadavg 手动查询 CPU 负载。

总体来说 CPU 使用率衡量的是 CPU 自身花费的时间;而 CPU 负载衡量的是安排给 CPU 的任务。 比方说执行大量重 I/O 的线程可能会导致很高 CPU 负载高,但 CPU 使用率可能反而很低。

另外,对于多核 CPU(暂时不考虑超线程),衡量整机的 CPU 使用率与负载时,通常都是以核心数为比例进行缩放的。 比方说对于 8 核 CPU 而言,整机 CPU 使用率的上限是 800%。

提供一些参考资料:

内存

# 查询当前内存占用情况
free -h

# 监控一段时间内内存占用变化情况
watch -n 1 --differences=cumulative 'free -h'

可以看到内存与交换分区的使用情况。

  • total:总内存。
  • used:已使用的内存,比如 malloc 分配的内存,满足 used = total - free - buff/cache
  • free:完全空闲、未使用的内存,通常会注意到这个值比较小,这个是正常现象,因为 Linux 会充分利用空闲内存进行缓存以提高性能。
  • shared:进程间共享的内存,一个经典的例子就是 PostgreSQL 中的 shared_buffers
  • buff/cache:一般来说是指临时使用的内存,在需要时可被内核回收。
    • buffers:用于内核缓存。
    • cache:用于页缓存与 slab,可以使用命令 echo 3 > /proc/sys/vm/drop_caches 回收页缓存与 slab 对象。
  • available:系统估算出的可供使用的内存,包括 free 与可回收的 buff/cache

各 NUMA 结点上的内存

可以参考 NUMA 相关命令速查

# 监控一段时间内各 NUMA 节点内存分配的命中情况
watch -n 1 --differences=cumulative numastat

# 监控一段时间内各 NUMA 节点内存占用变化情况
watch -n 1 --differences=cumulative 'numastat -m -c | grep -e Node -e MemFree -e MemUsed'

硬盘

查询硬盘信息:

# 查询所有硬盘
fdisk -l

# 查询硬盘挂载
df -h

脏页回写

通过以下内核参数(对应的文件在 /proc/sys/vm 目录中)修改脏页回写策略:

  • vm.dirty_background_ratio:脏页超过该比例时会被背景线程异步回写。
  • vm.dirty_ratio:脏页超过该比例时,写请求会被阻塞,直至部分脏页被回写到硬盘。
  • vm.dirty_expire_centisecs:脏页停留时间(厘秒),超过该时间后脏页会被背景线程异步回写。
  • vm.dirty_writeback_centisecs:负责回写的背景线程唤醒的周期(厘秒)。

例如:

sysctl -w vm.dirty_ratio=60

查询当前脏页数量:

grep nr_dirty /proc/vmstat

I/O 统计

可以用 iostat 查询硬盘的 I/O 统计信息。 比如下面的命令会查询 nvme0n1nvme1n1 这两块盘的 I/O 统计信息,末尾两个数字用于指定每 2s 查询一次,共查询 5 次:

iostat nvme0n1 nvme1n1 2 5

输出大概像是这样:

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
           1.82    0.00    0.34    0.06    0.00   97.78

Device             tps    kB_read/s    kB_wrtn/s    kB_dscd/s    kB_read    kB_wrtn    kB_dscd
nvme0n1        4243.82        73.92    433912.70   1853392.66     124665  731763388 3125616988
nvme1n1         233.50         3.91     29609.24   1853392.66       6601   49933904 3125616988

还可以带上 -x 参数以显示扩展信息:

iostat -x nvme0n1 nvme1n1 2 5
  • avg-cpu:平均 CPU 使用率,可参考上文。

  • tps:transfers per second,一个 transfer 就是一个 I/O 请求。

  • kB_read/s:读吞吐量。

  • kB_wrtn/s:写吞吐量。

  • kB_dscd/s:设备每秒丢弃的数据量(以 kB 为单位)

    对于机械硬盘来说,硬盘空间可由软件维护,在写入时直接覆盖就行,操作系统在释放空间时不需要通知硬盘,这种情况下 kB_dscd/s 会恒定为零。 但固态硬盘就不一样,因为固态硬盘有一个写入放大的问题:写入的最小单位是一个数据块,即使少量字节的写入也会导致整个数据块的覆写。 因此释放固态硬盘上的空间时,操作系统需要向硬盘发送 trim 命令以指定需要释放的空间。 固态硬盘在后续对数据块进行覆盖写时就可以依据 trim 命令提供的信息减少覆写时的拷贝开销。具体可以参考 Trim (computing) - Wikipedia

  • await:I/O 请求从提交至设备到处理完成的平均等待时间,包括 I/O 请求在队列中的等待时间与设备实际处理时间。

  • r_await:读请求的平均等待时间。

  • w_await:写请求的平均等待时间。

  • %util:设备在采样周期内处理 I/O 请求的时间比例。

    对于串行处理的设备来说,接近 100% 意味着该设备的饱和。 但对于具有并行处理能力的设备(比如 NVMe)来说,100% 并不能准确反映设备的性能瓶颈。

网络

查询网卡的硬件信息:

# 通过 ip 查询网络接口
dev="$(ip address | grep $ip | awk '{ print $NF }')"

# 查询网卡带宽等信息
ethtool "${dev}"

ping

确认两台设备间连接情况,可以使用 ping

ping <ip>

大致输出:

PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=109 time=40.2 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=109 time=40.1 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=109 time=40.0 ms
  • ttl:time to live。

    剩余跳数,每经过一跳路由会减 1,减至 0 时该数据包会被丢弃。 其初始值由目标系统决定,Windows 一般是 128,Linux 一般是 64。 在内网环境下分离部署应用时,需要尽量减少途径的路由。 如果剩余跳数偏小,说明经过的路由比较多,需要检查网络拓扑。

  • time:往返时延。

    内网环境下,时延最好小于 0.1ms。

sar

sar 命令可用于监控网卡 I/O 统计,需要启动 sysstat 服务。 比如下面的命令会查询所有网卡的统计信息,末尾两个数字用于指定每 2s 查询一次,共查询 5 次:

sar -n DEV 2 5
  • rxpck/stxpck/s:每秒接收与发送的包数。
  • rxkB/stxkB/s:接收与发送方向的吞吐量。
  • rxcmp/stxcmp/s:每秒接收与发送方向的数据包数量。
  • %ifutil:网络接口的使用率,是吞吐量与带宽的比值。