操作系统调优:内核参数优化、CPU亲和性与隔离、内存与Swap管理

操作系统调优,说白了就是给交易系统铺一条高速公路。内核参数、CPU、内存这三样,哪一样没弄好,系统都可能在高并发下直接趴窝。我这些年调过的交易系统,十有八九的问题都出在这三层。

一、内核参数优化:别让默认值坑了你

Linux 内核的默认参数,是为通用场景设计的。交易系统这种高并发、低延迟的场景,很多默认值简直就是灾难。我个人习惯,拿到一台新机器,第一件事就是改内核参数。

1. 网络栈参数

交易系统最怕网络延迟。我见过一个项目,订单量一上来,客户端就开始报连接超时。查了半天,发现是 net.core.somaxconn 还是默认的 128。你想想看,128 的 backlog,在高并发下分分钟就满了。

# 网络核心参数
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
net.core.netdev_max_backlog = 65535

# TCP 快速回收与重用
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 0  # 注意:NAT 环境下不要开
net.ipv4.tcp_fin_timeout = 15

# 扩大端口范围
net.ipv4.ip_local_port_range = 1024 65535

避坑指南:我曾经在某个云环境里开了 tcp_tw_recycle,结果 NAT 后面的客户端全部连不上。从那以后,我只要看到这个参数,都会先确认网络环境。

2. 文件句柄与进程限制

交易系统通常要开大量连接,文件句柄不够用是常事。我记得有一次线上排查,发现 broker 进程突然不响应了,lsof 一看,文件句柄用满了。

# /etc/security/limits.conf
* soft nofile 1048576
* hard nofile 1048576
* soft nproc 65536
* hard nproc 65536

3. 虚拟内存参数

这里有个关键参数 vm.swappiness。默认值是 60,意味着系统会倾向于使用 swap。对于交易系统来说,swap 就是性能杀手。我建议直接设为 1 或 0。

vm.swappiness = 1
vm.dirty_ratio = 20
vm.dirty_background_ratio = 5

小技巧vm.dirty_ratio 控制脏页占内存的比例。交易系统写日志频繁,设得太小会导致频繁刷盘,设得太大又怕断电丢数据。20% 是我在多个项目中验证过的平衡点。

二、CPU亲和性与隔离:让核心各司其职

CPU 亲和性,说白了就是把进程绑定到特定核心上。为什么要这么做?因为 CPU 缓存。进程在不同核心间切换,L1/L2 缓存就废了,性能直接打折扣。

1. 中断亲和性

网卡中断是 CPU 的大敌。默认情况下,所有中断都落在 CPU 0 上。你想想看,CPU 0 既要处理中断,又要跑业务逻辑,不卡才怪。

# 查看中断分布
cat /proc/interrupts | grep eth0

# 设置中断亲和性(以 CPU 2-3 为例)
echo 0c > /proc/irq/<中断号>/smp_affinity

我个人习惯,把网卡中断绑定到 CPU 0-1,业务进程绑定到 CPU 2-7。这样中断和业务互不干扰。

2. CPU 隔离

如果追求极致性能,可以用 isolcpus 内核参数把某些核心完全隔离出来。这些核心不参与进程调度,只跑你指定的进程。

# 在 GRUB 启动参数中添加
isolcpus=2,3,4,5,6,7

# 然后通过 taskset 绑定进程
taskset -c 2,3 ./trading_engine

注意:隔离 CPU 后,这些核心上的 kworker 等内核线程也会被移走。我曾经遇到过一个问题,隔离了 CPU 后,某个驱动模块的定时器无法触发,排查了很久才发现是内核线程被隔离了。

3. NUMA 亲和性

多路服务器上,NUMA 架构会导致跨节点访问内存延迟很高。交易系统一定要绑定 NUMA 节点。

# 查看 NUMA 拓扑
numactl --hardware

# 绑定进程到指定节点
numactl --cpunodebind=0 --membind=0 ./trading_engine

三、内存与Swap管理:别让内存成为瓶颈

内存管理这块,交易系统最怕两件事:一是内存泄漏,二是 swap 抖动。

1. 大页内存

交易系统通常使用大页内存(HugePages)。为什么?因为普通 4KB 页表太大,TLB 缓存命中率低。用 2MB 的大页,TLB 覆盖的内存范围能扩大 512 倍。

# 配置大页内存(以 2MB 页为例,分配 1024 个)
echo 1024 > /proc/sys/vm/nr_hugepages

# 查看大页使用情况
cat /proc/meminfo | grep Huge

# Java 应用启用大页
-XX:+UseLargePages -XX:LargePageSizeInBytes=2m

经验之谈:大页内存一旦分配,就不能被换出。所以分配多少要算清楚。我一般会留 10% 的余量给普通页,防止某些库不支持大页。

2. Swap 管理

交易系统要不要开 swap?我的答案是:不开。或者只在极端情况下开一点点。

为什么?因为 swap 的延迟是内存的几十万倍。一旦发生 swap,交易延迟就会从微秒级跳到毫秒级。对于高频交易来说,这简直是灾难。

# 关闭 swap
swapoff -a

# 或者设置 swappiness 为 0
echo 0 > /proc/sys/vm/swappiness

我曾经踩过的坑:有一次为了省内存,开了 swap,还设了 swappiness=10。结果某次行情波动,内存使用率突然升高,系统开始 swap,交易引擎的延迟从 50 微秒直接飙到 5 毫秒。从那以后,我所有交易系统都关 swap。

3. 内存锁

对于关键进程,可以用 mlockall 把内存锁在物理内存中,防止被换出。

// C 代码示例
#include <sys/mman.h>
mlockall(MCL_CURRENT | MCL_FUTURE);

四、知识体系总览

下面这张图,是我对操作系统调优的总结。你可以把它当作一个检查清单。

操作系统调优知识体系 内核参数优化 • 网络栈参数 somaxconn / tcp_tw_reuse • 文件句柄限制 nofile / nproc • 虚拟内存参数 swappiness / dirty_ratio • 内核调度参数 CPU亲和性与隔离 • 中断亲和性 smp_affinity 设置 • CPU 隔离 isolcpus / taskset • NUMA 亲和性 numactl 绑定 • 进程/线程绑定 内存与Swap管理 • 大页内存 HugePages 配置 • Swap 管理 swapoff / swappiness • 内存锁 mlockall 防换出 • OOM 管理 核心原则 1. 减少上下文切换:中断隔离、CPU绑定 2. 避免内存抖动:关Swap、用大页、锁内存 3. 消除瓶颈点:网络参数、文件句柄、NUMA

五、实战检查清单

最后,我分享一份检查清单。每次部署交易系统前,我都会过一遍。

检查项 推荐值 验证命令
somaxconn 65535 sysctl net.core.somaxconn
文件句柄限制 1048576 ulimit -n
swappiness 0 或 1 sysctl vm.swappiness
大页内存 根据应用分配 cat /proc/meminfo | grep Huge
中断亲和性 绑定到专用核心 cat /proc/interrupts
NUMA 绑定 进程与内存同节点 numactl --show

一句话总结:操作系统调优没有银弹。内核参数、CPU、内存这三块,每一块都要根据业务场景反复测试。我见过太多人只调了内核参数,忽略了 CPU 亲和性,结果性能还是上不去。记住,这三者是联动的,缺一不可。

专注资料整理