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 知识体系总览
下面这张图,我把本章的核心知识点串起来了。你可以把它当作一个检查清单,部署前逐项确认。
嗯,操作系统层面的优化,说到底就是让内核更好地配合你的应用。别想着一次调完就万事大吉。业务在变,流量在变,参数也得跟着调。我一般每季度会重新审视一次这些配置,看看有没有新的优化空间。
记住一点:调优不是炫技,是解决问题。别为了调而调,搞清楚每个参数背后的原理,再动手。
本章小结:
- 网络参数:重点调
somaxconn、tcp_tw_reuse,别碰tcp_tw_recycle - 文件描述符:用户级和系统级都要改,别忘了systemd服务
- 进程调度:优先用CPU亲和性和nice值,实时调度慎用
想要更多资料,联系可可