跳转至

Vertica 服务器 IRQ Affinity 配置

作者:JiangChong | 撰写时间:2025年11月

适用场景:当你发现 Vertica 数据库整体性能下降、节点间网络延迟升高,但集群 CPU 使用率并不高时——问题可能出在中断处理上。本文讲解什么是 IRQ Affinity(中断亲和性),如何诊断中断风暴,以及多队列网卡的 SMP Affinity 设置方法。

关联文章:

理解全文脉络: 本文从「什么是中断亲和性」讲起(第 1 节),然后教你如何通过 /proc 文件系统和系统工具诊断中断分布不均(第 2-3 节),接着给出 irqbalance 自动均衡和手动 smp_affinity 两种解决方案(第 4 节),最后通过真实案例(第 5 节)和完整演练(第 6 节)将知识点串联为可操作的诊断流程。


1. 原理理解

1.1 什么是中断(IRQ)

当网卡收到一个数据包时,它不能直接把数据塞进 CPU——必须通过一个称为 中断(Interrupt Request,IRQ) 的机制通知 CPU。流程如下:

网卡收到数据包
  → 网卡硬件触发硬中断(hardirq),告诉 CPU「有数据来了」
  → CPU 暂停当前工作,执行中断处理程序
  → 中断处理程序将数据包放入内核网络协议栈的输入队列
  → 触发软中断(softirq),由内核协议栈(ksoftirqd)处理数据包
  → 数据包进入 socket buffer → 应用程序读取

比喻: 想象一个餐厅前台只有一个人接电话(CPU 处理中断)。如果所有订餐电话都打到这一个号码上,前台会忙得没时间处理任何事情——这就是中断风暴的本质。

1.2 什么是 IRQ Affinity(中断亲和性)

IRQ Affinity 指的是将一个硬件中断号(IRQ)绑定到特定的一个或多个 CPU 核心上处理。在 Linux 中,通过 /proc/irq/<IRQ号>/smp_affinity 文件来控制。

smp_affinity 的值是一个十六进制位掩码。每一位代表一个 CPU:

值(十六进制) 二进制 绑定的 CPU
001 0000 0001 CPU0
002 0000 0010 CPU1
004 0000 0100 CPU2
008 0000 1000 CPU3
010 0001 0000 CPU4
00f 0000 1111 CPU0-3
fff 1111 1111 1111 CPU0-11(12 个核心)

1.3 为什么中断亲和性对 Vertica 至关重要

Vertica 是一个 MPP 数据库,节点间通过 Spread(UDP 4803)和数据通道(TCP 5434)频繁通信。在高吞吐量万兆网络中,单节点每秒可能处理数万到数十万个网络中断。

当中断全部绑定在单个 CPU 上时:

  1. 该 CPU 几乎将所有时间花在 softirq(软中断处理)上——top 显示 %si 接近 100%
  2. 其他 CPU 处于空闲状态,但无法分担中断处理工作
  3. 中断处理延迟增加 → 数据包在网卡 Ring Buffer 中排队等待
  4. 如果 Ring Buffer 也满了 → rx_drops → 数据包丢弃
  5. 对 Vertica 的影响:Spread UDP Token 延迟增大 → Token 超时 → 节点被驱逐 → 集群宕机或性能退化

这就是某运营商 2021 年性能问题的根因——所有网卡中断集中在 CPU8 上,单个 CPU 满载但整体 CPU 使用率仅 30%,排查时容易被误判为「CPU 资源充足」。

1.4 单队列网卡 vs 多队列网卡

特性 单队列网卡 多队列网卡(RSS)
中断号数量 1 个 每个队列 1 个(如 63 个队列 = 63 个中断号)
中断均衡方式 只能绑定到 1 个或少量 CPU 每个队列可绑定到不同 CPU
CPU 利用率 单核可能成为瓶颈 可充分利用多核
典型网卡 老式千兆网卡 Intel X710 / Mellanox 万兆网卡
队列数检查 N/A ethtool -l eth0

多队列网卡是实现真正中断负载均衡的前提。 单队列网卡只有一个中断号,即使使用 irqbalance,也只是将这个中断轮转到不同 CPU,无法实现并行处理。

1.5 中断问题的来源总结

来源 具体问题 典型症状
irqbalance 未启用 系统默认未启动 irqbalance 服务 所有中断集中在 CPU0
smp_affinity 手动设置后未恢复 运维曾手动绑定中断到特定 CPU,之后忘记恢复 部分 CPU 空闲,某个 CPU 100% softirq
网卡队列数不足 多队列网卡未调整队列数到最大值 中断无法充分利用多核
NUMA 跨节点 网卡中断绑定在远端 NUMA 节点的 CPU 上 中断处理延迟增大(需要跨 NUMA 访问内存)
中断与 Vertica 进程争抢 CPU 中断绑定在 Vertica 资源池预留的 CPU 上 查询和中断互相抢占

2. 系统级监控

2.1 从 top/mpstat 查看软中断占比

在怀疑中断问题之前,先用 topmpstat 看全局 CPU 分布。关键看 %si(softirq,软中断)列:

# 用 mpstat 查看每个 CPU 的中断统计(%soft 列即软中断)
mpstat -P ALL 1 5

如何解读结果:

  • %si 接近 0% → 正常,软中断开销很小
  • 某个 CPU 的 %si > 50% → 该 CPU 几乎将所有时间花在处理软中断上,极有可能是中断风暴
  • 多个 CPU 的 %si 都比较高 → 这是正常的(中断已均衡),但如果有专用 CPU 空闲而其他 CPU 满载,说明均衡仍有优化空间
  • %hi(硬中断)通常很小(< 1%),如果很高说明某些硬件在大量产生硬中断

2.2 从 /proc/interrupts 查看中断分布

/proc/interrupts 是诊断中断问题的核心文件。它列出了每个中断号在各 CPU 上的累计计数,以及对应的设备:

# 查看所有中断的分布(重点关注网卡行)
cat /proc/interrupts | grep -E "CPU|eth|enp|ens"

# 间隔 2 秒再次查看,通过差值判断增量集中在哪些 CPU
cat /proc/interrupts | grep -E "CPU|eth|enp|ens"
sleep 2
cat /proc/interrupts | grep -E "CPU|eth|enp|ens"

# 更简洁:只看特定网卡的中断分布
cat /proc/interrupts | grep -E "CPU|enp0s"

如何解读结果:

输出示例(多队列网卡):

           CPU0     CPU1     CPU2     CPU3     CPU4     CPU5     CPU6     CPU7
enp5f0-0   1245     0        0        0        0        0        0        0
enp5f0-1   0        1382     0        0        0        0        0        0
enp5f0-2   0        0        1156     0        0        0        0        0
enp5f0-3   0        0        0        1410     0        0        0        0
  • 所有队列的计数分散在不同 CPU 上 → 中断已均衡,正常
  • 所有队列的计数集中在同一个 CPU 上(如全部在 CPU0)→ 中断风暴风险,需要均衡
  • 只有 1 个网卡队列行(如 eth0,没有 -0-1 后缀)→ 这是单队列网卡,中断均衡能力有限

2.3 从 /proc/irq/*/smp_affinity 查看亲和性掩码

每个中断号对应一个 /proc/irq/<IRQ号>/smp_affinity 文件,记录该中断绑定了哪些 CPU:

# 列出所有网卡相关中断的亲和性设置
for irq in $(ls /proc/irq/); do
    aff=$(cat /proc/irq/$irq/smp_affinity 2>/dev/null)
    [ -n "$aff" ] && echo "IRQ $irq: $aff"
done | head -20

# 更精准:只查看网卡相关中断
grep -E "eth|enp|ens" /proc/interrupts | awk '{print $1}' | sed 's/:$//' | while read irq; do
    echo -n "IRQ $irq: "
    cat /proc/irq/$irq/smp_affinity 2>/dev/null
done

如何解读结果:

  • smp_affinity = fff(12 位全 1)→ 该中断可以路由到 CPU0-11 中的任意一个,由硬件自动选择
  • smp_affinity = 001 → 该中断只能由 CPU0 处理
  • 所有网卡 IRQ 的 smp_affinity 都是相同的小值(如全部 001)→ 中断被锁定在单个 CPU,需要立即处理

2.4 检查 irqbalance 服务状态

irqbalance 是 Linux 的后台守护进程,负责自动将中断均衡分配到各 CPU:

# 检查 irqbalance 是否在运行
systemctl status irqbalance

# 检查 irqbalance 是否已设为开机启动
systemctl is-enabled irqbalance

如何解读:

  • Active: active (running) → irqbalance 正在运行,中断应该已被自动均衡(但不绝对——某些情况下 irqbalance 的决策可能不理想)
  • Active: inactive (dead) → irqbalance 未运行,中断可能集中在 CPU0 或手动设置的 CPU 上
  • enabled → 开机自动启动
  • disabled → 开机不会自动启动,重启后需要手动启动

2.5 Vertica 层面:从系统表看网络问题征兆

虽然 Vertica 系统表不直接显示 IRQ 信息,但可以通过网络错误间接判断是否存在中断处理不及时的问题:

-- 查看网卡丢包(rx_drops 持续增长暗示中断处理跟不上)
SELECT
    time,
    node_name,
    interface_id,
    rx_drops,
    tx_drops,
    rx_packets,
    CASE WHEN rx_packets > 0
         THEN ROUND(rx_drops::numeric / rx_packets * 100, 4)
         ELSE 0 END AS rx_drop_pct
FROM v_internal.dc_network_info
WHERE interface_id != 'lo'
  AND rx_drops > 0
  AND time >= CURRENT_TIMESTAMP - INTERVAL '1 hour'
ORDER BY time DESC, node_name;

如何解读rx_drops 持续增长 + 网卡 Ring Buffer 已调到最大 → 问题可能在中断处理端——CPU 来不及处理网络中断,数据包在 Ring Buffer 中堆积后被丢弃。


3. 逐步定位根因

3.1 第一步:确认是否为中断分布不均

做什么:快速判断是否存在「部分 CPU 满载处理中断、其他 CPU 空闲」的典型中断风暴模式。

命令:

# 第一步:查看每个 CPU 的软中断占比
mpstat -P ALL 1 5

如何解读

  • 如果某个 CPU 的 %si > 80% 而其他 CPU 的 %si < 5% → 确认中断分布不均
  • 如果所有 CPU 的 %si 都接近 0 → 中断不是当前瓶颈,排查其他方向(见关联文章)

如果不是 → 中断分布正常,无需继续本文的后续步骤。集群性能下降可能来自其他方面,参考 Vertica 集群网络性能优化与诊断最佳实践 的完整诊断链。

如果是 → 进入 3.2 节确认具体中断号。

3.2 第二步:找到问题中断号和网卡队列

做什么:找出哪些中断号集中在哪些 CPU 上,确认是否与网卡相关。

命令:

# 查看 /proc/interrupts 中网卡相关行的中断分布
# eth/enp/ens 覆盖了常见的网卡命名:传统 ethX、bonding slave 接口、PCIe 命名接口
cat /proc/interrupts | grep -E "CPU|eth|enp|ens"

如何解读

  • 网卡行(如 ens3f0-0ens3f0-63)的中断计数全部指向同一 CPU → 中断集中
  • 网卡只有 1 行(没有队列后缀如 -0-1)→ 单队列网卡,均衡能力有限。调大 Ring Buffer 并考虑升级硬件
  • 网卡队列数(行数)远少于 CPU 核心数 → 可能可以增加队列数(见 4.3 节)

3.3 第三步:检查网卡队列数

做什么:确认多队列网卡是否启用了所有可用队列,队列数是否与 CPU 核心数匹配。

命令:

# 查看当前队列数和硬件支持的最大队列数
ethtool -l eth0

# 查看网卡型号和驱动
ethtool -i eth0

如何解读输出示例:

Channel parameters for ens3f0:
Pre-set maximums:
RX:             0
TX:             0
Other:          0
Combined:       63        ← 硬件支持最多 63 个队列
Current hardware settings:
RX:             0
TX:             0
Other:          0
Combined:       1         ← 当前只用了 1 个队列!
  • 如果 Current Combined 远小于 Pre-set maximums Combined → 网卡队列未被充分利用
  • 队列数决定了中断号的个数——1 个队列 = 1 个中断号,即使有 64 个 CPU,中断也只能由 1 个 CPU 处理
  • 队列数不必等于 CPU 总数,但应该至少覆盖 NUMA 节点内的核心数

3.4 第四步:确认 irqbalance 状态与中断绑定关系

做什么:irqbalance 运行时,手动设置 smp_affinity 会被它覆盖。需要明确当前是谁在管理中断分配。

命令:

# 确认 irqbalance 状态
systemctl status irqbalance

# 查看当前网卡中断的 affinity 设置
grep -E "eth|enp|ens" /proc/interrupts | awk '{print $1}' | sed 's/:$//' | while read irq; do
    aff=$(cat /proc/irq/$irq/smp_affinity 2>/dev/null)
    aff_list=$(cat /proc/irq/$irq/smp_affinity_list 2>/dev/null)
    echo "IRQ $irq: mask=$aff, CPUs=$aff_list"
done

# 查看 irqbalance 的配置(如果有)
cat /etc/sysconfig/irqbalance 2>/dev/null || cat /etc/default/irqbalance 2>/dev/null

如何解读结果:

  • irqbalance 运行中 + smp_affinity = fff 这样的全宽掩码 → irqbalance 在工作,中断由硬件自动分发
  • irqbalance 未运行 + smp_affinity 都是小值 → 手动设置过且未被恢复,这是某运营商案例的情况
  • irqbalance 运行但 smp_affinity 仍是小值 → irqbalance 可能被配置为某些中断不处理(IRQBALANCE_BANNED_CPUSIRQBALANCE_BANNED_INTERRUPTS

4. 解决方案

4.1 立即措施:启动 irqbalance 自动均衡

这是最推荐、最简单、最安全的方案。 irqbalance 是一个成熟的 Linux 守护进程,会持续监控中断负载并动态调整亲和性。

# 立即启动 irqbalance
systemctl start irqbalance

# 设为开机自启
systemctl enable irqbalance

# 确认生效——等待 5-10 秒后再次检查中断分布
sleep 10
cat /proc/interrupts | grep -E "CPU|eth|enp|ens"

为什么推荐 irqbalance 而非手动设置:

  • 自动感知变化:当 CPU 负载变化、网卡队列数调整后,irqbalance 会自动重新分配,无需人工干预
  • NUMA 感知:现代 irqbalance(v1.0.3+)支持 NUMA 感知,会将网卡中断优先分配到同 NUMA 节点的 CPU 上
  • 避免配置漂移:手动绑定在系统重启、网卡驱动重载后可能失效,irqbalance 持久可靠
  • 大多数发行版默认已安装yum install irqbalanceapt install irqbalance

irqbalance 的高级配置选项(仅在需要精细控制时使用):

# /etc/sysconfig/irqbalance 或 /etc/default/irqbalance
# 排除某些 CPU 不参与中断处理(如保留给 Vertica 专用)
# 注意:BANNED_CPUS 是十六进制位掩码,BANNED_CPULIST 才支持逗号/范围格式
IRQBALANCE_BANNED_CPULIST="0,1"

# 排除某些中断不被 irqbalance 管理(如已手动绑定的)
IRQBALANCE_BANNED_INTERRUPTS="95,96"

# 使用 NUMA 感知模式(默认开启,通常无需修改)
IRQBALANCE_ARGS="--hintpolicy=subset"

如果 irqbalance 不可用(容器环境、最小化安装),进入 4.2 节手动设置。

4.2 手动设置:SMP Affinity 脚本

如果需要精细控制(如多 NUMA 节点服务器需要将特定网卡队列绑定到本地 NUMA 节点的 CPU),可以手动编写亲和性设置脚本。

场景一:单队列网卡——将中断绑定到多个 CPU,由硬件轮转分配:

#!/bin/bash
# 获取网卡的中断号
IRQ=$(grep -E "eth0$" /proc/interrupts | awk -F: '{print $1}')
# 将中断绑定到 CPU0-7(mask = 0xff = 二进制 11111111)
echo ff > /proc/irq/$IRQ/smp_affinity

场景二:多队列网卡——每个队列绑定到不同的 CPU:

#!/bin/bash
# 适用于 Intel X710 等万兆多队列网卡
# 假设 ens3f0 有 8 个队列,CPU 0-7 位于同一 NUMA 节点

# 获取网卡的所有中断号
IRQS=$(grep "ens3f0" /proc/interrupts | awk -F: '{print $1}')

# 将每个队列的中断绑定到不同 CPU
CPU=0
for irq in $IRQS; do
    # 将 CPU 号转换为十六进制掩码(CPU0=1, CPU1=2, CPU2=4, CPU3=8...)
    MASK=$(printf "%x" $((1 << CPU)))
    echo "$MASK" > /proc/irq/$irq/smp_affinity
    echo "IRQ $irq -> CPU$CPU (mask=$MASK)"
    CPU=$((CPU + 1))
done

场景三:NUMA 感知绑定——将网卡中断绑定到同 NUMA 节点的 CPU:

#!/bin/bash
# 假设 ens3f0 位于 NUMA node0,该节点有 CPU 0-7, 16-23(超线程开启时)
# 仅绑定到物理核心 CPU 0-7,跳过超线程核心

IRQS=$(grep "ens3f0" /proc/interrupts | awk -F: '{print $1}')
CPUS=(0 1 2 3 4 5 6 7)

CPU_IDX=0
for irq in $IRQS; do
    CPU=${CPUS[$CPU_IDX]}
    MASK=$(printf "%x" $((1 << CPU)))
    echo "$MASK" > /proc/irq/$irq/smp_affinity
    echo "IRQ $irq -> CPU$CPU"
    CPU_IDX=$(( (CPU_IDX + 1) % ${#CPUS[@]} ))
done

持久化设置: echo 写入的 smp_affinity 在重启后失效。如果无法使用 irqbalance,需要将上述脚本写入 systemd oneshot 服务或 NetworkManager dispatcher 脚本,确保每次启动后自动执行。也可以用 rc.local(CentOS 7/SUSE 11 等老系统),但 RHEL 8+ 已弃用 rc.local。

4.3 短期优化:增大网卡队列数

# 查看支持的最大队列数
ethtool -l eth0

# 将队列数设为最大值(如 63)
ethtool -L eth0 combined 63

# 调整后重启 irqbalance 使其重新分配
systemctl restart irqbalance

为什么需要足够多的队列:每个队列对应一个中断号。如果有 64 个 CPU 但只有 4 个队列,即使 irqbalance 完美工作,也只能将中断均衡到 4 个 CPU 上——其他 60 个 CPU 无法参与中断处理。

队列数选择建议

场景 建议队列数
千兆网卡 4-8
万兆网卡 16-32
25G/40G 网卡 32-63
有 NUMA 绑定需求 等于单 NUMA 节点内物理核心数

4.4 进阶:中断与 Vertica 进程隔离

在极端高吞吐量场景中,网卡中断处理和 Vertica 查询线程可能争抢同一 CPU。通过 cgroup 将中断和 Vertica 隔离到不同 CPU 组,可以彻底消除互相干扰:

# 将 irqbalance 的中断处理限制在特定 CPU 组(避免占用 Vertica 的 CPU)
# /etc/sysconfig/irqbalance
IRQBALANCE_BANNED_CPULIST="0-5"   # 中断不使用 CPU0-5(留给 Vertica)

# 同时,将 Vertica 进程通过 cgroup 限制在 CPU0-5
# 参考 Spread Debugging 5.6 节

注意:中断与进程隔离是高端优化手段,不建议作为默认配置。只在中断风暴反复出现、irqbalance 无法解决的极端高吞吐量场景中使用。错误配置可能导致中断处理能力不足,反而加剧问题。


5. 深入案例

5.1 真实案例:网卡中断集中 CPU8 导致集群整体性能下降

📋 真实案例

客户行业:某通信运营商

集群规模:50 节点,Vertica 7.2.3-17,SUSE 11,CPU 为 2×Intel Xeon E5-2650 v4(24 核 48 线程),内存 256GB,磁盘 19TB/node,网络为 4×1Gb 网卡(2 组 bonding)

故障现象:业务侧反馈数据库性能整体变慢。初步检查发现集群硬件资源使用率不高——内存和磁盘远未耗尽,CPU 整体使用率仅 30% 左右。

诊断过程

  1. 由于整体资源不高但性能变慢,怀疑存在单点瓶颈。检查每个 CPU 核心的使用率
  2. 发现部分 CPU 使用率达到 100%——但仅是个别核心,整体平均值正常
  3. 进一步检查 /proc/interrupts——发现所有网络中断处理都集中绑定在 CPU8 上
  4. 其他 CPU 的中断计数几乎为 0,CPU8 承载了所有网卡中断(包括 bonding 下的两个 slave 接口的所有流量)

根因:网卡中断未均衡。中断处理器 smp_affinity 被设置为仅使用 CPU8。在 1Gb 网络环境中(4 个 1Gb 口 = 4 Gbps 总带宽),CPU8 需要处理所有网卡中断的同时还要执行 Vertica 查询线程 → CPU8 严重过载 → 中断处理延迟增大 → 网络延迟上升 → Spread Token 传递变慢 → MPP 集群的「木桶效应」下,最慢节点拖慢全局。

修复方案:调整网卡中断的 SMP Affinity 设置,将中断均衡分配到所有 CPU 核心。

效果:修复后网络延迟恢复正常,集群性能恢复。这是一个仅需一行配置改动就能解决全局性能问题的经典案例。

教训

  • top 显示的整体 CPU 使用率不等于没有 CPU 瓶颈——需要查看单个核心的使用率
  • /proc/interrupts 是排查「CPU 看似有富余但数据库反应慢」的必查文件
  • 单核 %si 100% 是中断风暴的典型特征

5.2 虚构案例:irqbalance 意外停止导致节点间通信延迟

📝 虚构案例

场景描述:某金融机构 16 节点 Vertica Eon 集群,万兆网卡(Intel X710,63 个队列)。某天凌晨 ETL 期间,SQL 执行时间突然从平时的 3-5 分钟飙升至 20-30 分钟。白天恢复正常,但一连三天同一时间复现。

诊断过程

SQL 层面确认是网络问题:

-- 查看 ETL 时段网络流量
SELECT
    node_name,
    end_time,
    net_tx_kbytes_per_second,
    net_rx_kbytes_per_second
FROM v_monitor.system_resource_usage
WHERE end_time BETWEEN '2026-06-13 02:00:00' AND '2026-06-13 03:00:00'
ORDER BY net_tx_kbytes_per_second DESC
LIMIT 10;

输出显示流量正常(~500,000 KB/s),带宽未跑满。进入 Linux 层面排查:

mpstat -P ALL 1 10

输出:

CPU    %usr  %sys  %iowait  %irq  %soft  %idle
CPU0   15.2   8.3    0.0    0.2    3.5   72.8
CPU1    0.3   0.2    0.0    0.0  100.0    0.0   ← CPU1 softirq 100%!
CPU2    0.5   0.3    0.0    0.0  100.0    0.0   ← CPU2 softirq 100%!
CPU3   12.1   6.5    0.0    0.1    2.2   79.1
...

CPU1 和 CPU2 的 %soft 达到 100%。进一步检查:

cat /proc/interrupts | grep -E "CPU|ens3f0"

输出:

         CPU0  CPU1  CPU2  CPU3 ...
ens3f0-0   0  52341    0     0
ens3f0-1   0      0 48921    0
ens3f0-2   0  50123    0     0   ← 又落在 CPU1!
ens3f0-3   0      0 47321    0   ← 又落在 CPU2!
ens3f0-4   0  49876    0     0
...(所有 63 个队列全部集中在 CPU1 和 CPU2)

所有 63 个网卡队列的中断全部落在 CPU1 和 CPU2 上。 检查 irqbalance:

systemctl status irqbalance
# Active: inactive (dead) since 2026-06-13 01:30

irqbalance 在凌晨 1:30 停止了——正好是 ETL 开始前半小时。进一步排查发现,是前一天的 OS 安全补丁升级触发了 systemd 的依赖冲突,导致 irqbalance 服务被意外停止。

根因:irqbalance 停止后,所有网卡队列中断回落到了默认的亲和性设置——只绑定到 CPU1 和 CPU2。ETL 期间大量数据加载(COPY)和中间结果 Resegment 产生巨大的网络流量,CPU1 和 CPU2 全部时间花在软中断处理上,网络数据包处理延迟增大,导致涉及这些节点的查询变慢。

修复方案

systemctl restart irqbalance
systemctl enable irqbalance

效果:irqbalance 重启后 10 秒内中断重新均衡到所有 16 个 CPU 核心,ETL 执行时间恢复到 3-5 分钟。

教训:irqbalance 是 Vertica 服务器的「必装服务」,需要在监控系统中设置 irqbalance 服务的存活告警。


5.3 虚构案例:手动绑定的遗留配置引发 NUMA 远端访问延迟

📝 虚构案例

场景描述:某互联网公司 100 节点 Vertica Enterprise 集群,万兆网卡。运维团队曾因一次性能问题手动设置了网卡中断的 smp_affinity。问题修复后已过去 8 个月,配置一直遗留在服务器上。最近集群扩容(新增 20 个节点),新节点的网络性能明显优于老节点(vnetperf UDP 800 MB/s vs 450 MB/s)。

诊断过程

对比新老节点的配置:

# 老节点:
cat /proc/interrupts | grep -E "CPU|ens5f0"
# 输出:
#         CPU0  CPU1  CPU2  CPU3  CPU4  CPU5  CPU6  CPU7  CPU8-15
# ens5f0   ...   ...   ...   ...  大量   大量   大量   大量   均为0
# 中断集中在 CPU4-7(一个 NUMA 节点的物理核心)

# 新节点:
# 中断均匀分布在 CPU0-15 所有核心

# 确认网卡 PCIe NUMA 位置
cat /sys/class/net/ens5f0/device/numa_node
# 输出:1 → ens5f0 位于 NUMA node1(CPU 8-15)!

发现:老节点的网卡(ens5f0)位于 NUMA node1(CPU 8-15),但 8 个月前手动设置的中断亲和性却将中断绑定到了 NUMA node0 的 CPU 4-7。这意味着:

  1. 网卡通过 PCIe 连接到 NUMA node1
  2. 网卡用 DMA 将数据写入 NUMA node1 的内存
  3. 但 CPU 4-7(NUMA node0)必须跨 NUMA 节点访问这些数据
  4. 跨 NUMA 内存访问的延迟是本地 NUMA 访问的 1.5-2 倍

根因:历史遗留的手动中断亲和性配置导致了 NUMA 远端访问。irqbalance 被手动配置覆盖后,中断持续绑定在不合理的 CPU 上。

修复方案

# 清除手动绑定,重启 irqbalance 让它重新 NUMA 感知式分配
systemctl restart irqbalance
# irqbalance 默认会优先将网卡中断分配给同 NUMA 节点的 CPU

效果:修复后 vnetperf UDP 吞吐量从 450 MB/s 提升到 780 MB/s,接近新节点的水平。

教训

  • 手动设置 smp_affinity 后需要记录并定期审查,避免成为技术债务
  • 网卡和中断处理 CPU 位于不同 NUMA 节点会显著增加延迟
  • 在 NUMA 系统中,irqbalance --hintpolicy=subset 是比手动绑定更好的选择

5.4 虚构案例:单队列网卡在万兆环境下成为瓶颈

📝 虚构案例

场景描述:某中小企业 5 节点 Vertica Eon 集群,每个节点使用 2 张万兆网卡(Intel X710)做 bonding,总带宽 20 Gbps。业务高峰期查询偶尔变慢,vnetperf 测试正常(UDP 800+ MB/s),但 v_internal.dc_network_inforx_drops 持续增长。

诊断过程

# 检查网卡队列数
ethtool -l ens2f0
# 输出:
# Combined: 1   ← 只有 1 个队列!!!

# 但硬件支持:
# Pre-set maximums: Combined: 63

问题:网卡虽为 X710 万兆网卡,但队列数被设为 1。63 个硬件队列被禁用,仅剩 1 个队列。即使用 bonding 将两个网卡捆绑为 20 Gbps,但单个队列对应 1 个中断号 → 1 个 CPU 处理中断。

# 确认单队列导致的中断集中
cat /proc/interrupts | grep ens2f0
# 输出:只有 1 行(ens2f0),没有队列后缀
# CPU0: 83,452,731  其他 CPU: 0

虽然 irqbalance 在运行,但单队列网卡只有一个中断号,irqbalance 能做的只是将这个中断轮转到不同 CPU——无法实现多 CPU 并行处理。

修复方案

# 将网卡队列数设为最大值
ethtool -L ens2f0 combined 63
ethtool -L ens2f1 combined 63

# 重启 irqbalance 使其重新分配
systemctl restart irqbalance

# 验证:现在应该有 63 行 ens2f0-0 到 ens2f0-62
cat /proc/interrupts | grep ens2f0 | wc -l
# 输出:63

效果:调整后 rx_drops 停止增长,中断均匀分布到 64 个 CPU(63 个队列 + 1 个管理中断)。查询延迟降低了 15%。

教训万兆网卡 + 单队列 = 浪费硬件能力。 队列数调整应在网卡初始化时就设置为最大值。


6. 完整诊断流程实战

📝 虚构场景 · 完整演练

背景:某金融机构 24 节点 Vertica Enterprise 集群(K-Safety=1),万兆网络(Intel X710 双口 bonding),每条服务器 2×Intel Xeon Gold 6330N(28 核 56 线程),内存 512GB。运维收到告警:最近一周业务高峰期(上午 9:00-11:00),多张核心汇总表查询时间从平时的 5-10 秒增加到 30-60 秒,但 CPU/内存/磁盘使用率均正常。

时间线:

时间 操作 发现
09:30 接到告警 高峰时段查询变慢,但资源使用率正常
09:35 mpstat -P ALL 1 5 CPU14 的 %soft = 97%,CPU15 的 %soft = 92%,其他 CPU %soft < 5%
09:40 cat /proc/interrupts \| grep ens 确认:bonding 下 4 个 slave 接口(ens1f0, ens1f1, ens2f0, ens2f1)的所有中断集中在 CPU14 和 CPU15
09:42 ethtool -l ens1f0 队列数:Combined = 1(硬件支持 63),严重不足
09:45 systemctl status irqbalance irqbalance 未运行(半年前的维护中被手动停止后未重启)
09:50 v_internal.dc_network_info 查询 高峰期 rx_drops > 0,中断处理不及时导致 Ring Buffer 溢出
10:00 根因确认 irqbalance 未运行 + 网卡队列数仅 1 → 2 个 CPU 处理所有网络中断 → 中断处理延迟 → 网络通信变慢 → 集群性能退化
10:05 执行修复 ethtool -L ens1f0 combined 63; ethtool -L ens1f1 combined 63; ethtool -L ens2f0 combined 63; ethtool -L ens2f1 combined 63
10:06 启动 irqbalance systemctl start irqbalance && systemctl enable irqbalance
10:10 验证 cat /proc/interrupts \| grep ens 显示 63×4=252 个队列行,中断均匀分布在所有 CPU
10:15 持久化 创建 systemd oneshot 服务确保队列数在重启后保持(irqbalance 已启用不会丢失)
次日 效果验证 高峰时段查询时间恢复至 5-10 秒,CPU %soft 分布均匀(均 < 5%),rx_drops = 0

关键诊断 SQL 汇总:

-- 步骤 1:确认高峰时段网络丢包
SELECT
    time,
    node_name,
    interface_id,
    rx_drops,
    rx_packets
FROM v_internal.dc_network_info
WHERE interface_id != 'lo'
  AND rx_drops > 0
  AND time BETWEEN '2026-06-13 09:00:00' AND '2026-06-13 11:00:00'
ORDER BY time DESC, node_name;

-- 步骤 2:确认高峰时段的网络流量模式
SELECT
    node_name,
    end_time,
    net_rx_kbytes_per_second,
    net_tx_kbytes_per_second,
    net_rx_kbytes_per_second + net_tx_kbytes_per_second AS total_kb_s
FROM v_monitor.system_resource_usage
WHERE end_time BETWEEN '2026-06-13 09:00:00' AND '2026-06-13 11:00:00'
ORDER BY total_kb_s DESC
LIMIT 20;

7. 快速诊断命令工具箱

诊断目标 命令 说明
查看 CPU 软中断占比 mpstat -P ALL 1 5 重点关注 %soft 列,任何 CPU 的 %soft > 50% 需立即排查
查看中断在各 CPU 的分布 cat /proc/interrupts \| grep -E "CPU\|eth\|enp\|ens" 网卡行的计数是否集中在少数 CPU
查看中断亲和性掩码 grep -E "eth\|enp\|ens" /proc/interrupts \| awk -F: '{print $1}' \| while read irq; do echo -n "IRQ $irq: "; cat /proc/irq/$irq/smp_affinity 2>/dev/null; done 如果所有掩码相同且为小值,说明未均衡
查看中断亲和性的 CPU 列表(可读格式) grep -E "eth\|enp\|ens" /proc/interrupts \| awk -F: '{print $1}' \| while read irq; do echo -n "IRQ $irq: "; cat /proc/irq/$irq/smp_affinity_list 2>/dev/null; done 比十六进制掩码更直观
检查 irqbalance 服务 systemctl status irqbalance active (running) = 正常
查看网卡队列数 ethtool -l eth0 Combined: current vs maximum
查看网卡驱动和型号 ethtool -i eth0 用于判断硬件能力
查看网卡硬件错误 ethtool -S eth0 \| grep -E "drop\|error\|discard" 从硬件层面确认丢包
检查 NUMA 拓扑 lscpu \| grep -E "NUMA\|CPU\(s\)" 用于 NUMA 感知的手动绑定
查看网卡 PCIe NUMA 位置 cat /sys/class/net/eth0/device/numa_node -1 表示无 NUMA 信息,0/1 表示 NUMA node
Vertica 网络丢包确认 SELECT time, node_name, interface_id, rx_drops FROM v_internal.dc_network_info WHERE rx_drops > 0 AND time >= CURRENT_TIMESTAMP - INTERVAL '1 hour' ORDER BY time DESC; 从数据库层面确认
启动 irqbalance systemctl start irqbalance && systemctl enable irqbalance 立即生效 + 持久化
增大网卡队列数 ethtool -L eth0 combined <最大值> 然后 systemctl restart irqbalance

8. 最佳实践清单

序号 实践 为什么
1 部署 Vertica 前确保 irqbalance 已启动并设为开机自启 irqbalance 是最小投入最大收益的中断均衡方案。它自动感知 CPU 负载变化和 NUMA 拓扑,比手动绑定可靠得多。在 OS 装机时就启用,避免成为事后补丁
2 检查 /proc/interrupts 是排查「CPU 看似有富余但数据库慢」的必做步骤 top 显示的 CPU 使用率是所有核心的平均值。单核 %soft 100% 不会被平均后的总使用率暴露出来。某运营商案例中整体 CPU 仅 30%,但中断全部集中在 CPU8
3 万兆网卡部署时将队列数设为硬件最大值 1 个队列 = 1 个中断号 = 1 个 CPU 处理中断。63 个队列 = 63 个中断号 = 多 CPU 并行处理。队列数调整没有副作用,越大越好
4 避免手动设置 smp_affinity 作为默认配置 手动绑定在系统重启、网卡驱动重载后可能失效。irqbalance 能满足 95% 的场景。只有在 irqbalance 无法满足的极端场景(如需要 NUMA 精确绑定的高性能计算)才手动设置
5 如果必须手动设置,记录并纳入配置管理 遗留手动配置在被发现前可能已经影响性能数月。所有手动 smp_affinity 设置应通过配置管理工具(Ansible/Puppet)管理,确保可追溯
6 网卡中断不要与 Vertica 资源池预留的 CPU 冲突 如果通过 cpuaffinityset 预留了 CPU 给 Vertica,不要将网卡中断绑定到这些 CPU 上。两者会互相抢占
7 NUMA 系统尽量将中断分配在同 NUMA 节点的 CPU 上 跨 NUMA 访问内存的延迟是本地访问的 1.5-2 倍。irqbalance --hintpolicy=subset 默认会优先本地 NUMA。手动绑定时需用 cat /sys/class/net/eth0/device/numa_node 确认 PCIe 位置
8 监控 CPU %soft 指标并设置告警 单个 CPU 的 %soft > 50% 持续 5 分钟应触发告警。这比「数据库变慢」的用户投诉提前数小时甚至数天
9 调整网卡队列数后必须重启 irqbalance 队列数变化后中断号也变了,irqbalance 需要重新读取中断列表才能对新中断号进行均衡分配
10 单队列网卡不能仅靠 irqbalance 解决中断问题 单队列网卡只有一个中断号,irqbalance 只能将其轮转到不同 CPU——无法并行处理。如果流量持续高于 ~2 Gbps,考虑升级到多队列网卡

扩展阅读