2、PSI指标详解:CPU、内存、IO三种资源的PSI指标含义与数据格式

PSI(Pressure Stall Information)是 Linux 内核 4.20 引入的度量机制,用于量化系统资源(CPU、内存、IO)的竞争程度。与传统的系统负载(Load Average)不同,PSI 能够精确区分“任务在等待资源”的时间比例,从而直接反映延迟尖峰的根因。

2.1 PSI 的核心设计思想

PSI 通过跟踪任务在可运行队列(runnable)或不可中断睡眠(IO 等待)状态下的等待时间,计算资源短缺导致的“压力”百分比。其核心指标分为两类:

  • some:至少有一个任务因资源不足而等待的时间占比。
  • full:所有任务都因资源不足而等待的时间占比(即系统完全停滞)。
关键理解: “some” 表示资源存在竞争,但系统仍在部分运行;“full” 表示资源已被完全耗尽,系统吞吐量降为零。在延迟尖峰告警中,“full” 指标通常比 “some” 更具告警价值。

2.2 三种资源的 PSI 指标含义

资源类型 some 含义 full 含义 典型场景
CPU 至少一个任务因 CPU 时间片不足而等待 所有可运行任务都在等待 CPU(通常不会发生,因为 idle 任务存在) 计算密集型任务争抢、调度延迟、CGroup 限制
内存 (IO) 至少一个任务因内存页回收或 swap 而等待 所有任务都在等待内存资源(系统几乎无响应) 内存泄漏、OOM 前兆、swap 风暴、大页分配失败
IO 至少一个任务因块设备 IO 完成而等待 所有任务都在等待 IO 完成(系统完全阻塞) 磁盘性能瓶颈、文件系统锁、存储网络延迟
注意: CPU 的 “full” 指标在大多数系统中始终为 0,因为 Linux 调度器保证至少有一个 idle 任务可运行。因此,CPU 压力告警应主要关注 “some” 指标,而内存和 IO 的 “full” 指标才是真正的“系统挂起”信号。

2.3 PSI 数据格式与文件结构

PSI 数据通过 /proc/pressure/ 目录下的三个伪文件暴露:

/proc/pressure/cpu
/proc/pressure/memory
/proc/pressure/io

每个文件的内容格式如下(以 /proc/pressure/cpu 为例):

some avg10=0.00 avg60=0.00 avg300=0.00 total=0
full avg10=0.00 avg60=0.00 avg300=0.00 total=0

字段说明:

  • avg10 / avg60 / avg300:过去 10 秒、60 秒、300 秒的平均压力百分比(浮点数,精确到小数点后两位)。
  • total:自系统启动以来的累计等待时间(微秒,单调递增)。
数据精度: avg10 用于快速检测瞬时尖峰,avg60 用于短期趋势,avg300 用于长期基线。在延迟尖峰告警中,avg10 是最敏感的指标,通常设置 0.1%~1% 的阈值即可捕获异常。

2.4 PSI 指标与延迟尖峰的关联

下图展示了 PSI 指标如何映射到应用延迟的恶化过程:

PSI 指标与延迟尖峰关联图 PSI 指标与延迟尖峰关联图 资源竞争加剧 CPU/内存/IO 争抢 PSI 指标上升 some/full 百分比增加 应用延迟尖峰 P99 延迟飙升 告警逻辑:PSI 指标是延迟尖峰的早期信号 当 PSI avg10 > 阈值时,资源竞争已开始影响任务调度 此时应用延迟尚未显著恶化,但已进入“压力积累”阶段 时间 → avg10 首次突破阈值 avg60 持续上升 延迟尖峰出现

2.5 PSI 数据读取与解析示例

以下 Python 代码演示如何读取并解析 PSI 指标:

import re

def read_psi(resource):
    """读取指定资源的 PSI 指标"""
    path = f"/proc/pressure/{resource}"
    with open(path, 'r') as f:
        data = f.read()
    return data

def parse_psi(data):
    """解析 PSI 数据为结构化字典"""
    result = {}
    for line in data.strip().split('\n'):
        parts = line.split()
        metric_type = parts[0].rstrip(':')  # 'some' 或 'full'
        avg10 = float(parts[1].split('=')[1])
        avg60 = float(parts[2].split('=')[1])
        avg300 = float(parts[3].split('=')[1])
        total = int(parts[4].split('=')[1])
        result[metric_type] = {
            'avg10': avg10,
            'avg60': avg60,
            'avg300': avg300,
            'total': total
        }
    return result

# 示例:读取内存 PSI
psi_data = read_psi('memory')
parsed = parse_psi(psi_data)
print(f"Memory some avg10: {parsed['some']['avg10']}%")
print(f"Memory full avg10: {parsed['full']['avg10']}%")

2.6 PSI 指标在延迟尖峰告警中的使用建议

资源 推荐告警指标 典型阈值(avg10) 说明
CPU some > 5% CPU 竞争通常不会导致 full,some 即可反映调度延迟
内存 full > 0.5% 内存 full 意味着任务被阻塞在回收路径,延迟影响极大
IO full > 1% IO full 表示所有任务都在等待磁盘,系统几乎停滞
最佳实践: 在设置告警阈值时,建议先收集 7 天的基线数据,以 avg10 的 P95 值作为参考。对于延迟敏感型服务(如数据库、消息队列),阈值应比通用服务更严格(例如内存 full avg10 > 0.1% 即告警)。

2.7 PSI 与 cgroup v2 的集成

在 cgroup v2 环境下,PSI 指标同样适用于容器级别。每个 cgroup 的 PSI 数据位于:

/sys/fs/cgroup/<cgroup_path>/cpu.pressure
/sys/fs/cgroup/<cgroup_path>/memory.pressure
/sys/fs/cgroup/<cgroup_path>/io.pressure

数据格式与系统级完全一致。这使得我们可以精确监控单个容器或 Pod 的资源压力,避免“邻居效应”导致的误告警。

注意: cgroup v2 的 PSI 指标默认启用,无需额外内核参数。但需要确保内核版本 >= 4.20,且 cgroup 文件系统已挂载为 v2 模式。