信创数据库服务器上,NUMA 绑核和网卡中断隔离是降低延迟抖动的关键。
原文标题:数据库信创服务器NUMA绑核策略
原文作者:牧羊人的方向
冷月清谈:
文中进一步拆解了 Linux 网卡收包流程,包括硬件队列、RSS、多队列、MSI-X、硬中断、软中断、NAPI 轮询和协议栈处理,解释了为什么高并发 OLTP 小包场景下,软中断会成为数据库延迟抖动的重要来源。
实践部分以双路鲲鹏 920 服务器为例,给出数据库实例按 Socket 隔离、网卡中断绑定到专用核、关闭 irqbalance、调整网卡队列数、扩大 Ring Buffer、关闭 NUMA 自动平衡等操作建议。文章也提醒,绑核会带来 CPU 使用上限,若主库算力不足,不建议简单跨 Socket 扩核,应优先优化 SQL、索引、并发模型或进行横向拆分。
怜星夜思:
2、网卡中断一定要和数据库业务核隔离吗?小流量场景有没有必要这么细?
3、网卡队列数是不是越多越好?为什么文章建议队列数匹配预留中断核?
4、关闭 irqbalance 会不会带来副作用?生产环境应该怎么控制风险?
原文内容
在信创基础设施规模化落地的背景下,海光、鲲鹏等架构服务器已成为信创数据库部署的主流硬件底座。这两类CPU均采用NUMA(非一致性内存访问)架构,CPU访问本地节点内存的延迟与带宽远优于远端节点,如果沿用默认的系统调度策略,数据库进程与网卡中断会在不同NUMA节点间漂移,引发缓存失效、跨节点内存开销、资源争抢等问题,最终导致数据库性能下降、延迟抖动加剧。本文将介绍在数据库信创服务器下,数据库进程该如何绑定NUMA节点、网卡中断是否需要绑核又该如何绑核。
1、NUMA绑核分析
NUMA架构将整台服务器划分为多个独立的NUMA节点,每个节点拥有专属的CPU核、内存控制器与本地内存,节点间通过片间互联总线通信。CPU访问同节点本地内存的延迟最低、带宽最高;访问跨节点内存需经过互联总线,延迟显著升高、带宽下降。
海光与鲲鹏平台的NUMA架构存在明显差异,绑核策略需针对性适配:
-
海光平台:单颗CPU集成4个DIE,每个DIE对应1个NUMA节点,双路服务器共8个NUMA节点,单节点通常包含8~16个CPU核。同Socket内跨DIE访问开销较低,跨Socket访问开销显著升高,NUMA颗粒度更细。
-
鲲鹏平台:单颗CPU包含24个NUMA节点,双路服务器共48个节点,全系无超线程技术,所有核均为物理核。鲲鹏平台跨NUMA节点的内存访问延迟高于x86平台,跨Socket访问延迟可达同节点的2.5~3倍,NUMA亲和性优先级更高。
关于NUMA相关知识,参考“”。
数据库属于典型的内存密集型+计算密集型负载,绑核的核心作用是从硬件层面消除NUMA架构带来的性能损耗。
-
消除跨NUMA内存访问开销:数据库的共享内存(缓冲池、共享池、日志缓冲区等)容量通常达到几十GB甚至上百GB,如果进程在不同NUMA节点间漂移,会频繁触发远端内存访问。绑核后进程与内存固定在对应NUMA节点内,可将内存访问延迟降低50%以上。
-
降低CPU调度与缓存失效开销:默认内核调度会将线程在不同CPU核间迁移,导致CPU的L1、L2、L3级缓存数据频繁失效,需要重新从内存加载。数据库线程固定在指定CPU核运行后,可充分利用缓存预热结果,指令与数据命中率大幅提升。
-
减少高并发下的上下文切换:高并发场景下数据库线程数量众多,无约束的调度会导致大量上下文切换,占用有效算力。绑核后线程调度范围被限定在指定CPU核内,可减少60%以上的上下文切换,提升CPU有效利用率。
对于不同服务器类型,海光支持SMT(超线程),两个逻辑线程共享执行单元,如果不绑核,两个数据库线程可能挤在同一物理核上,互相阻塞;鲲鹏无超线程,所有核均为物理核,但单颗CPU内包含多个DIE(呈现为多个NUMA节点),不绑核同样面临跨DIE访问。
服务器的PCIe网卡物理挂载在特定CPU的PCIe控制器上,对应归属一个固定的NUMA节点,网卡的DMA内存、硬件中断默认优先分配在所属节点。默认系统调度下,中断处理可以调度到任意CPU核,可能会引发一系列性能问题。
-
避免中断漂移,提升CPU缓存命中率:中断频繁在不同CPU核间迁移,会导致网络协议栈、驱动程序的缓存数据频繁失效,处理延迟升高。将中断固定在指定CPU后,缓存可持续预热,网络包处理效率提升20%以上。
-
保障NUMA亲和性,降低内存访问延迟:若中断调度到远端NUMA节点的CPU核处理,数据包读取、协议栈解析都会涉及跨节点内存访问,延迟大幅升高。将中断绑定到网卡所属NUMA节点的CPU核,可实现“网卡DMA写入→中断处理→协议栈解析”全流程本地内存访问,性能最优。
-
多队列并行扩展,突破单核中断瓶颈:单个CPU核的中断处理能力存在上限,万兆以上网卡需开启多队列技术,每个队列对应独立中断,绑定不同核实现并行处理,吞吐量随核数近似线性提升。
-
业务与中断资源隔离,消除性能抖动:高流量场景下,网卡软中断会占用大量CPU算力。将中断固定在专用CPU核,与数据库业务CPU核完全隔离,可避免中断处理抢占数据库算力,消除业务延迟抖动,提升性能稳定性。
2、网卡中断处理流程
1)网卡硬件队列(RX/TX Queue)
网卡硬件队列是网卡芯片上的硬件缓存队列,与内存中的环形缓冲区(Ring Buffer)一一对应,是数据在硬件与内核之间的中转站。
-
接收队列(RX Queue):负责存放网卡收到的、等待内核处理的数据包;
-
发送队列(TX Queue):负责存放内核提交的、等待网卡发送的数据包。
每个队列都对应一块预先分配好的内存(Ring Buffer),通过DMA(直接内存访问) 技术映射给网卡:网卡可以不经过CPU,直接把数据包写入内存的Ring Buffer,也可以直接从Ring Buffer读取数据发送,极大降低CPU占用。
早期网卡只有1个RX队列,所有流量都由这一个队列处理,对应1个中断,只能由单个CPU处理。随着网卡带宽从千兆升级到万兆以上,单核的中断处理能力已经成为瓶颈(单核极限约100万pps小包)。因此诞生了多队列网卡(配合MSI-X中断技术),多队列网卡支持多个独立的 RX/TX 队列,每个队列对应一个独立的硬件中断号;通过RSS(接收端缩放)技术,网卡基于数据包的源IP、目的IP、源端口、目的端口、协议号(五元组)做哈希计算,将同一条流的数据包固定分发到同一个RX队列,实现流量的并行分发;另外不同队列的中断可以绑定到不同CPU,实现多核并行处理,吞吐量随CPU核数近似线性提升。
2)中断的两个层级:硬中断vs软中断
Linux将中断处理拆分为硬中断和软中断,以平衡 “快速响应硬件” 和 “充分处理数据” 的需求。硬中断是网卡硬件主动发送信号给CPU、软中断则是内核软件触发。
3)NAPI机制
NAPI是Linux内核针对高流量网络场景的优化机制,核心思想是“中断触发+轮询处理”,解决高并发下“中断风暴”导致的CPU开销过高问题。
-
低流量时:正常走中断模式,每个数据包触发一次中断,响应及时;
-
高流量时:硬中断触发后,暂时关闭该队列的硬件中断,内核通过轮询(poll)的方式批量处理队列里的数据包,处理完或达到处理上限后,再重新开启中断;
这种机制在高流量下可以大幅减少中断次数,降低上下文切换开销,提升吞吐量。
网卡接收流程是中断、队列交互最复杂的链路,按数据包流向分为5个阶段。
-
数据包通过光纤/网线到达网卡物理接口,PHY芯片完成光电转换、物理层解码;MAC层完成帧校验、MAC地址过滤,丢弃错误帧、非本机帧。
-
RSS队列分发:网卡硬件根据数据包的五元组(源IP、目的IP、源端口、目的端口、协议号)计算哈希值,选择对应的RX硬件队列(保证同一条TCP流始终进入同一个队列,避免乱序)。
-
DMA直接内存写入:网卡不经过CPU,直接通过DMA总线,将完整的数据包写入该RX队列对应的内存Ring Buffer中(Ring Buffer是内核预先分配、与网卡映射的连续内存块,以描述符链表形式组织)。
-
中断触发准备:数据包写入完成后,网卡向CPU发送该队列对应的MSI-X硬件中断信号,通知CPU“有新数据包到达,请处理”。
-
CPU收到中断信号后,立即暂停当前正在执行的任务,保存寄存器上下文,跳转到该中断号对应的中断处理函数(ISR)。
-
硬中断处理函数仅执行3个简单的操作:
-
向网卡寄存器写入应答,告知硬件“中断已收到”;
-
禁用该RX队列的硬件中断(防止后续数据包持续触发中断,引发中断风暴,为NAPI轮询做准备);
-
标记该队列的NAPI状态为“待调度”,触发内核NET_RX_SOFTIRQ(网络接收软中断)。
-
硬中断执行完毕,CPU恢复之前的上下文,继续执行被打断的任务。
软中断处理会占用大量CPU算力。
-
软中断调度触发:硬中断返回后,内核会检查软中断待处理标记;如果当前CPU有未处理的网络软中断,会立即进入软中断处理流程;如果当前CPU繁忙,则由对应核的ksoftirqd/n内核线程(n为CPU号)调度处理。
-
NAPI poll 轮询:调用该RX队列驱动的poll函数,开始批量从Ring Buffer中读取数据包:从Ring Buffer的头部开始,逐个取出数据包描述符,构建内核网络结构体sk_buff(简称skb,Linux内核中网络数据包的通用载体);每处理完一个描述符,就释放Ring Buffer中对应的内存位置,更新队列指针,供网卡后续写入新数据包;
-
轮询退出条件(满足任一即退出本次poll):Ring Buffer中的数据包全部处理完毕,队列为空;达到单次软中断的处理预算(默认netdev_budget=64个包,或2ms时间上限),避免长时间占用 CPU。
-
退出后处理:如果队列已空:重新开启该RX队列的硬件中断,退出NAPI模式,等待下一次硬中断触发;如果还有未处理数据包:保持中断关闭,继续标记软中断待处理,下一次调度时继续轮询。
数据库OLTP场景多为小包(SQL请求通常几百字节),软中断处理的占比会非常高。将网卡中断绑定到固定CPU,本质是固定软中断的处理CPU,提升CPU缓存命中率,同时避免抢占数据库业务CPU。
sk_buff离开驱动层后,向上进入内核协议栈,逐层解析处理:
-
链路层:校验以太网头,处理VLAN标签,判断帧类型(IP/ARP等),对应转发到上层;
-
网络层(IP层):解析IP头,校验IP地址,经过iptables/netfilter钩子,判断数据包是本机接收还是转发;
-
传输层(TCP/UDP):解析端口号,校验TCP/UDP头,处理序列号、滑动窗口、拥塞控制等TCP逻辑;
-
socket匹配:根据“源IP+目的IP+源端口+目的端口+协议”五元组,查找对应的内核socket对象。
-
数据包被放入对应socket的接收缓冲区(Receive Buffer)中,排队等待应用读取;
-
内核唤醒阻塞在该socket上的用户态进程(如数据库的监听进程、工作线程);
-
数据库进程通过read()/recv()等系统调用,将数据从内核态socket缓冲区拷贝到用户态内存中,完成一次完整的数据包接收。
1)关闭irqbalance
在软中断(SoftIRQ)阶段,CPU会消耗大量算力处理协议栈。irqbalance会周期性地把硬件中断(Hard IRQ)迁移到不同的CPU核。中断迁移导致网卡驱动上下文、RSS表、软中断状态在不同CPU的L1/L2 Cache之间频繁失效。因此关闭 irqbalance,手动将MSI-X中断(smp_affinity)绑定在特定的NUMA节点上。
2)中断处理核和数据库业务核分开
在高并发访问下,软中断(si%)会占用多个CPU。如果和业务同核,软中断会频繁抢占数据库线程的CPU时间片,导致数据库P99延迟剧烈抖动。此时需要将中断绑定到专用CPU,数据库绑定到其他CPU。
3)调整Ring Buffer大小
如果RX Ring Buffer太小(如默认256),在突发流量(Microburst)时,网卡DMA来不及写入,或者CPU软中断poll来不及取走,就会导致Ring Buffer溢出(Drop),网卡直接丢包。
4)网卡队列数量与NUMA节点核数不匹配
网卡开启的RX队列总数,超过了网卡所属NUMA节点的总核数。网卡默认配置将队列数设为整机CPU总核数,但网卡仅挂载在单个NUMA节点上。比如海光双路服务器共8个NUMA节点、64核,网卡默认开64个队列,但网卡仅属于NUMA0节点(仅8核),数量严重不匹配。这种配置上的不匹配有可能会导致数据库性能上TPS/QPS上不去但网络延迟大、偶发重传或丢包连接超时、CPU占用不均等问题。
-
单核多队列,处理效率骤降:多个队列的中断只能挤在少数核上,单个核要处理多个队列的NAPI轮询,上下文切换、缓存失效大幅增加,软中断处理不及时,严重时导致Ring Buffer溢出丢包。
-
RSS哈希效率下降:过多队列会导致五元组哈希碰撞概率升高,同一条流的数据包可能被分发到不同队列,引发TCP乱序,进一步降低业务性能。
优化建议是设置合理的队列数:队列数=所属NUMA节点预留的中断核数,建议占单节点核数的10%~25%。
3、信创服务器绑核策略
以鲲鹏双路鲲鹏920共128C为例,共4个NUMA节点(Node 0 ~ Node 3),单节点32核。单台服务器部署1主1备两个实例。Socket归属:
-
Socket 0(CPU0):包含Node 0(CPU 0~31)、Node 1(CPU 32~63),共64核
-
Socket 1(CPU1):包含Node 2(CPU 64~95)、Node 3(CPU 96~127),共64核
由于鲲鹏跨Socket内存访问延迟是同节点的2.5~3倍,远高于x86平台,因此所有进程、中断、内存分配必须严格控制在所属Socket内,严禁跨Socket绑定;1主1备两个数据库实例分属不同Socket,完全独占CPU、缓存、内存控制器、互联总线资源;网卡中断与数据库业务CPU无重叠,避免中断处理抢占数据库算力,消除业务延迟抖动;网卡队列数=预留中断CPU核数,且全部属于网卡所在NUMA节点,杜绝队列和CPU不匹配问题。
基于以上原则,单网卡预留4核做中断处理,每NUMA节点预留1核做系统基础服务(内核线程、监控、SSH 等),CPU核划分如下:
1)查看NUMA节点分布
#numactl --hardware
2)数据库进程绑核操作
#数据库实例 A(Socket 0,Node 0+Node 1) numactl -C 5-31,33-63 --membind=0,1 /path/to/db1#数据库实例 B(Socket 1,Node 2+Node 3)
numactl -C 69-95,97-127 –membind=2,3 /path/to/db2
#验证内存NUMA分布,查看numa_miss是否接近0(接近0表示本地内存命中率高)
numastat -p <数据库PID>
3)网卡中断绑核
#关闭自动中断调度irqbalance服务 systemctl stop irqbalance systemctl disable irqbalance#获取网卡中断号
#查看eth0的RX队列中断号
grep eth0 /proc/interrupts
#查看eth1的RX队列中断号
grep eth1 /proc/interrupts#执行脚本进行批量绑核
eth0的4个中断,绑Node0(CPU 1-4)
bash bind_irq.sh eth0 1 4 #绑定4个队列到CPU 1,2,3,4
eth1同理,绑定到Node2(CPU 65-68)
bash bind_irq.sh eth1 65 4 #绑定4个队列到CPU 65,66,67,68
查看中断对应的亲和性列表,确认CPU号是否正确
cat /proc/irq/xx/smp_affinity_list
bind_irq.sh如下,建议将网卡中断绑核操作配置到开机自启动中,这样服务器重启后也能自动绑核。
#!/bin/bash # 用法: bash bind_irq.sh <网卡名> <起始CPU> <核数> NIC=$1; START_CPU=$2; NUM_CPUS=$3 IRQS=($(grep "${NIC}" /proc/interrupts | awk '{print $1}' | sed 's/://g'))
for i in $(seq 0 $((${#IRQS[@]} - 1))); do
IRQ=${IRQS[$i]}
TARGET_CPU=$(( START_CPU + (i % NUM_CPUS) ))
# 使用 python 计算 2 的 TARGET_CPU 次方的十六进制,避免 bc 大数溢出
MASK=$(python3 -c “print(hex(1 << $TARGET_CPU)[2:])”)
echo $MASK > /proc/irq/$IRQ/smp_affinity
echo “IRQ $IRQ -> CPU $TARGET_CPU”
done
注:本方案无需开启 RPS/RFS。硬件多队列已足够承载数据库流量,且中断已固定CPU核,RPS 反而会增加跨核调度开销,保持默认关闭即可。
4)网卡队列配置
网卡队列数和网卡中断绑核数一一对应,避免多队列争抢单核。另外鲲鹏平台数据库场景不建议设置过多队列(超过8核收益会骤降),过多队列可能会导致RSS哈希碰撞率上升,TCP流乱序,反而会降低性能。
#查看网卡当前队列配置 ethtool -l eth0 ethtool -l eth1#设置combined队列数(RX/TX共用队列,对应中断数)
eth0设置4个队列,匹配Node0的4个中断核
ethtool -L eth0 combined 4
eth1设置4个队列,匹配Node2的4个中断核
ethtool -L eth1 combined 4
#调大Ring Buffer(防止突发流量丢包,数据库OLTP小包场景推荐)
ethtool -G eth0 rx 4096 tx 4096
ethtool -G eth1 rx 4096 tx 4096
5)其它OS层测参数优化
# 关闭NUMA自动平衡,避免内核自动迁移进程/内存打乱手动绑定 kernel.numa_balancing = 0开启NUMA节点内存回收,优先使用本地内存
vm.zone_reclaim_mode = 1
降低swap使用倾向,数据库场景尽量不使用交换分区
vm.swappiness = 0
TCP缓冲区优化,适配网卡大带宽
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
基于以上绑核和OS层的配置调优,可提升数据库TPS、降低事务延迟、提升网卡带宽利用率,降低数据库性能抖动。实际效果以压测结果为准。
在上述物理绑核的情况下,数据库主进程在物理上无法使用超过其绑定范围(即Chip 1的64个核)的CPU资源。但如果业务高并发访问的时候数据库主进程所需要的CPU超过64C时,将会出现什么情况?
-
TPS/QPS达到上限:数据库处理能力不再随并发增加而提升,新增请求会进入等待队列;
-
事务延迟增加:平均延迟、P99延迟显著上升,高并发下可能会出现大量连接超时、事务堆积,甚至引发数据库连接池打满、业务雪崩;
-
运行队列堆积:系统load average持续高于物理核数(64),大量线程处于等待CPU调度的状态,上下文切换次数呈指数级上升,调度开销进一步挤占有效算力。
此场景下所有计算仍在Socket 0内完成,内存访问均为本地节点,NUMA亲和性保持完好,不会出现跨节点的内存延迟问题,性能瓶颈是因为CPU不足导致。
如果放开绑核限制,让主库同时使用Socket 0和Socket 1的CPU,有可能会出现加CPU核数反而性能下降的现象。在鲲鹏 920的NUMA架构中,跨Socket的内存访问延迟是同节点2.5~3倍,且片间互联总线带宽远低于本地内存控制器。
-
内存访问延迟飙升:数据库的共享内存(缓冲池、共享池、日志缓冲区等)容量通常几十GB到上百GB,初始分配时集中在Socket 0的本地内存;当线程调度到Socket 1的CPU上时,所有对共享内存的访问都要跨片间总线,内存延迟从100ns级上升到300ns 以上,会拉低指令执行效率;
-
内存带宽瓶颈:单Socket拥有独立的内存控制器和通道,本地内存带宽远高于片间总线带宽;大量跨节点内存访问会打满互联总线,不仅拖慢主库自身性能,还会挤占同服务器其他进程的跨节点访问带宽。
根据鲲鹏服务器的实测数据,对于计算密集型的业务,跨Socket扩展CPU,性能有小幅提升,但提升幅度远低于核数增长比例。对于内存密集型场景(如缓冲池访问、全表扫描),跨节点扩展CPU可能会出现性能不升反降,同时延迟抖动会大幅加剧。另外,在高并发下尤为明显,并发越高,跨节点线程调度越频繁,CPU各级缓存失效越严重,性能损失比例越大。其它信创服务器如海光跨Socket扩展CPU,同样会出现性能收益递减,在数据库场景不推荐作为长期方案。
当主库CPU出现算力不足时,如果服务器层面已经不能再进行扩展,只能通过降低业务并发访问、优化慢SQL和增加索引的方式进行优化,甚至需要进行横向的扩容,将业务库进行拆分到多台服务器,以分散单台服务器的CPU压力。
参考资料:




