操作系统调优:内核参数优化、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 | 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 亲和性,结果性能还是上不去。记住,这三者是联动的,缺一不可。