3. 操作系统层面优化:Linux内核参数调优

说到吞吐优化,很多人第一反应是改代码、换硬件。但我得说,很多时候瓶颈其实藏在操作系统里。你想想看,应用层跑得再快,内核不给力,数据包到了门口进不去,那也白搭。

这一章,咱们就聊聊Linux内核参数调优。我把它分成三个核心维度:网络栈参数、文件描述符限制、进程调度策略。这三个点,我在生产环境里踩过不少坑,今天一并分享给你。

3.1 网络栈参数调优

网络吞吐,说白了就是数据包能不能顺畅地进出。内核里有一堆参数控制着这个流程。我挑几个最关键的讲。

3.1.1 net.core.somaxconn

这个参数控制的是TCP全连接队列的长度。什么意思呢?当你的应用调用listen()之后,内核会维护两个队列:半连接队列(SYN队列)和全连接队列(accept队列)。somaxconn就是全连接队列的上限。

我在项目中遇到过一个问题:某天线上服务突然大量报错connection refused。查了半天,发现是somaxconn默认值128,而业务高峰期并发连接数轻松超过这个数。队列满了,新连接直接被内核丢掉。

核心结论:对于高并发服务,建议将somaxconn调大到1024或更高,同时与应用层的backlog参数保持一致。

# 临时生效
echo 1024 > /proc/sys/net/core/somaxconn

# 永久生效(写入/etc/sysctl.conf)
net.core.somaxconn = 1024

小提示:调整后记得用ss -lnt查看Send-Q列,确认队列大小确实变了。我曾经改完参数忘了重启应用,白忙活半天。

3.1.2 tcp_tw_reuse 与 tcp_tw_recycle

这两个参数经常被放在一起讨论。它们都跟TIME_WAIT状态有关。TIME_WAIT是TCP四次挥手中的最后一个状态,持续2MSL(约60秒)。在高并发短连接场景下,大量TIME_WAIT连接会耗尽端口资源。

tcp_tw_reuse:允许将TIME_WAIT状态的socket用于新的连接。说白了就是复用。但有个前提——必须开启tcp_timestamps。

tcp_tw_recycle:更激进,直接加速TIME_WAIT的回收。但这个东西坑很多。我记得在NAT环境下吃过亏——客户端在内网,多个机器共享同一个公网IP,开启tcp_tw_recycle后,时间戳混乱导致连接被随机丢弃。

警告:从Linux 4.12开始,tcp_tw_recycle已经被移除。如果你还在用旧内核,我建议也别开它。老老实实用tcp_tw_reuse就好。

# 推荐配置
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_timestamps = 1
# 不要设置 tcp_tw_recycle

3.1.3 其他关键网络参数

参数名 默认值 推荐值 说明
net.ipv4.tcp_fin_timeout 60 15-30 FIN_WAIT2状态的超时时间,缩短可以减少连接残留
net.ipv4.tcp_keepalive_time 7200 600-1200 TCP保活探测间隔,对长连接服务很重要
net.core.rmem_default 212992 262144 接收缓冲区默认大小,影响吞吐
net.core.wmem_default 212992 262144 发送缓冲区默认大小
net.ipv4.tcp_syncookies 1 1 SYN Flood防护,建议保持开启

我的习惯:每次调完网络参数,我都会用sysctl -p重载配置,然后用sar -n DEV 1观察网络吞吐变化。数据说话,别靠感觉。

3.2 文件描述符限制

文件描述符(FD)是Linux系统里一个很宝贵的资源。每个TCP连接、每个打开的文件、每个socket,都要占用一个FD。如果FD不够用,应用就会报Too many open files。

我记得有一次线上告警,服务突然拒绝新连接。排查下来,发现是FD用完了。默认的1024限制,对于高并发服务来说,简直就是开玩笑。

3.2.1 用户级限制 vs 系统级限制

Linux的FD限制分两层:

  • 用户级:/etc/security/limits.conf,控制单个用户能打开的FD数
  • 系统级:/proc/sys/fs/file-max,控制整个系统能打开的FD总数

你想想看,只改用户级不改系统级,或者反过来,都会出问题。两个都得调。

# 用户级限制(/etc/security/limits.conf)
* soft nofile 65535
* hard nofile 65535

# 系统级限制(/etc/sysctl.conf)
fs.file-max = 1000000

注意:修改limits.conf后,需要重新登录才能生效。我建议用ulimit -n验证当前值。另外,systemd管理的服务需要在service文件中单独设置LimitNOFILE。

3.2.2 如何监控FD使用情况

别等到出问题了才去看FD。日常监控很重要。

# 查看进程FD使用量
lsof -p <PID> | wc -l

# 查看系统FD使用情况
cat /proc/sys/fs/file-nr
# 输出:已分配FD数  已使用FD数  最大FD数

我一般会在监控系统里加一个FD使用率的指标,超过80%就告警。这样能提前发现问题。

3.3 进程调度策略

吞吐优化不只是网络和IO的事,CPU调度也很关键。Linux内核用完全公平调度器(CFS)来分配CPU时间。但对于某些延迟敏感的服务,默认策略可能不够用。

3.3.1 调度策略与优先级

Linux支持多种调度策略:

策略 类型 适用场景
SCHED_OTHER 普通分时 大多数应用,默认策略
SCHED_FIFO 实时先入先出 需要低延迟的实时任务
SCHED_RR 实时轮转 类似FIFO,但有时间片限制
SCHED_BATCH 批处理 CPU密集型、非交互任务
SCHED_IDLE 空闲 低优先级后台任务

对于吞吐优化,我一般不会轻易动实时调度策略。为什么呢?因为SCHED_FIFO和SCHED_RR如果优先级设置不当,可能导致系统其他进程饿死。我曾经见过有人把Nginx设成SCHED_FIFO,结果SSH都连不上了。

避坑指南:我曾经在生产环境尝试给数据库进程设置SCHED_FIFO,结果CPU被独占,监控进程拿不到时间片,告警系统直接瘫痪。从那以后,我只有在明确知道自己在做什么的情况下,才碰实时调度。

3.3.2 实际调优建议

对于大多数服务,我建议这样做:

  • CPU亲和性:用taskset或numactl将关键进程绑定到特定CPU核心,减少上下文切换
  • nice值调整:用renice调整普通进程的优先级,范围-20到19,值越小优先级越高
  • cgroups限制:用cgroups v2的cpu.weight控制CPU分配比例
# 绑定进程到CPU 0-3
taskset -c 0-3 -p <PID>

# 调整nice值(降低优先级,让出CPU给其他进程)
renice +10 -p <PID>

3.4 知识体系总览

下面这张图,我把本章的核心知识点串起来了。你可以把它当作一个检查清单,部署前逐项确认。

操作系统层面优化:Linux内核参数调优 网络栈参数调优 文件描述符限制 进程调度策略 net.core.somaxconn tcp_tw_reuse / tcp_tw_recycle 缓冲区大小 / 超时参数 用户级限制 (limits.conf) 系统级限制 (file-max) 监控与告警 (file-nr) 调度策略 (FIFO/RR/OTHER) CPU亲和性 (taskset) nice值 / cgroups 核心原则:先监控,后调优,再验证 每次只改一个参数,观察效果后再继续

嗯,操作系统层面的优化,说到底就是让内核更好地配合你的应用。别想着一次调完就万事大吉。业务在变,流量在变,参数也得跟着调。我一般每季度会重新审视一次这些配置,看看有没有新的优化空间。

记住一点:调优不是炫技,是解决问题。别为了调而调,搞清楚每个参数背后的原理,再动手。

本章小结:

  • 网络参数:重点调somaxconn、tcp_tw_reuse,别碰tcp_tw_recycle
  • 文件描述符:用户级和系统级都要改,别忘了systemd服务
  • 进程调度:优先用CPU亲和性和nice值,实时调度慎用

想要更多资料,联系可可