RHEL9.7出来之后,不少团队从CentOS 7、8或者老一点的RHEL 8升级过来,第一反应就是“跑起来不如想象中快”。系统本身其实没毛病,默认配置扛不住生产负载才是真问题。我自己在几套RHEL9.7环境上做过一轮从内核参数、启动项、内存、存储到网络协议的完整优化,今天把整个过程和踩过的坑整理出来。这篇文章适合系统管理员、运维工程师,以及正在从旧发行版迁移到RHEL9系列的朋友,内容覆盖优化思路、实操命令、参数依据和回滚方法,照着做能少走很多弯路。
1. 优化前先摸清家底:环境盘点与基线制定
1.1 先别急着改参数,搞清楚你的工作负载
很多人拿到RHEL9.7第一件事就是网上找“优化脚本”一顿刷。我强烈不建议这么干。你连自己的业务是在跑数据库、跑Web服务、跑大数据计算还是做桌面办公都不知道,盲目优化等于盲人摸象。
先做一次完整的环境盘点,核心包括三件事。第一,确认系统版本和内核,可以直接用这几条命令:
bash复制cat /etc/os-release
uname -r
cat /proc/cmdline
第二,搞清硬件资源。lscpu看CPU型号和核数,free -h看内存,lsblk看磁盘类型,注意区分NVMe SSD、SATA SSD和机械盘,这三类盘的优化方向完全不一样。第三,也是最容易被忽略的:确认当前系统负载类型。用top看CPU的us(用户态)和wa(IO等待)比例,用vmstat 1 5看si/so(swap换入换出)和r(运行队列),用sar -d看磁盘util和await。我见过太多人上来就把swap调没,结果内存一紧张直接OOM杀进程,原因就是没搞清楚自己的业务到底吃不吃内存。
另外必须学会用tuned-adm active看一下当前激活的调优profile。RHEL9默认装好了tuned,这个工具相当于红帽官方帮你做了一轮基础优化,后面你的很多操作其实是在它基础上再做微调,而不是推倒重来。
1.2 建立优化基线的关键命令
没有基线就没有对比,你都不知道优化到底是变好了还是变坏了。我通常会在优化前记录一组数据,优化后再跑一遍同样的采集,拿数据说话。
需要用到的工具链包括:sysstat(sar、iostat)、perf(内核自带,做CPU热点分析)、fio(磁盘压测)、iperf3(网络吞吐测试)、stress(压力加载)。如果系统还没装,先装齐:
bash复制dnf install -y sysstat perf fio iperf3 stress
然后记录几组关键数据:
vmstat 1 10:看run队列、block、swap情况iostat -x 1 5:也是磁盘util、await、svctmss -s:当前TCP连接数uptime:负载均值sysctl -a | grep -E 'somaxconn|tcp_max_syn_backlog|dirty_ratio':记录当前的sysctl关键值
这里说个经验之谈:基线数据至少要连续采集三天,覆盖业务高峰和低谷。只看一个时间点的数据,很可能恰好错过问题窗口,也可能恰好采到异常值导致误判。我习惯用cron配合nmon或者sar的日志周期采集,等优化完再回头看那些长期曲线。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 内核参数与启动阶段优化:让系统在开机时就跑对方向
2.1 使用grubby调整内核启动参数
RHEL9系列不像老版本那样手工编辑/boot/grub2/grub.cfg,推荐直接用grubby命令改启动参数,改完还能自动重建grub配置。这个工具是红帽系特有的,比手工改稳妥得多。
我在RHEL9.7上最常调整的内核启动参数有三个。
第一个是mitigations=off。默认情况下,内核为缓解CPU漏洞(比如Meltdown、Spectre)会开启一系列代价高昂的缓解措施,对上下文切换频繁的应用性能影响很明显。如果你的机器是内网专用、不处理不可信代码,可以关掉这部分缓解来换回性能。但这条命令涉及安全取舍,我不建议直接在公网、多租户或者跑第三方代码的环境里使用。
第二个是intel_idle.max_cstate=1和processor.max_cstate=1。对延迟敏感的应用特别有用,它阻止CPU进入深度睡眠状态(C-state),降低了唤醒延迟,代价是待机功耗上升。数据库、缓存服务、高频交易这类场景受益很大,纯计算批处理任务就没必要动它。
第三个是transparent_hugepage=never。后面会详细展开,这里先提一句:如果你的业务是数据库或者大量随机小内存访问,建议启动时就关掉THP,避免运行中发生内存规整引发延迟尖峰。
实操命令长这样:
bash复制grubby --update-kernel=ALL --args="mitigations=off intel_idle.max_cstate=1 transparent_hugepage=never"
grubby --info=ALL | grep args
reboot
重启后用cat /proc/cmdline验证参数是否生效。注意:grubby --update-kernel=ALL会把你追加的参数写到所有内核条目上,如果你装过多个内核版本,这种方式最省心,不会因为内核更新导致参数丢失。
2.2 tuned调优profile的选择策略
RHEL9的tuned其实是个被低估的宝藏。它内置了十几种profile,分别针对不同场景做了大量微调。很多人不知道的是,这些profile不仅能调内核参数,还会调CPU governor、透明大页、磁盘IO调度器、网络buffer等一堆东西。
常用的几个profile和建议场景我用个表格说明:
| Profile名称 | 适用场景 | 核心调整 |
|---|---|---|
| throughput-performance | 默认profile之一,追求吞吐量 | CPU governor设为performance,禁用某些省电特性 |
| latency-performance | 低延迟优先 | 类似throughput,进一步降低休眠和定时器延迟 |
| network-latency | 网络延迟敏感应用 | 调整网络buffer、TCP参数、关闭节能 |
| virtual-guest | 虚拟机内部使用 | 针对虚拟化环境优化,减少不必要的hypervisor交互 |
| desktop | 桌面工作站,追求响应速度 | 提升交互响应,牺牲部分吞吐 |
切换方法十分简单:
bash复制tuned-adm profile latency-performance
tuned-adm active
我的建议是:别在默认的throughput-performance上硬改成手动sysctl,先看看latency-performance是否符合你的场景。RHEL9.7上的通过tuned-adm list能看所有可选择的profile,而且支持自定义profile。如果你需要组合多个场景,可以在/etc/tuned/下建一个自定义目录,写一个tuned.conf继承两个已有profile,比如既要网络低延迟又要虚拟化优化,可以这样:
code复制[main]
include=network-latency,virtual-guest
然后tuned-adm profile 你的自定义名字加载。这种方式的优点是所有参数改动都有记录、可通过tuned-adm profile一键恢复,比散落一堆sysctl脚本干净得多。
2.3 systemd服务层面的启动优化
内核参数搞定之后,看启动流程本身。systemd-analyze能给出很直观的启动耗时报告,systemd-analyze blame能看到具体哪个单元耗时最长。我见过不少机器开机要花50多秒,罪魁祸首往往就是某个无关紧要的网络挂载或者等待服务。
RHEL9.7默认开启的服务比CentOS 7时代要克制很多,但依然有几个我建议在生产上关掉的:
rhsmcertd:如果不用Red Hat Subscription Manager管理订阅,可以先停掉abrtd和abrt-journal-core:崩溃报告服务,排查问题时可以临时开,生产不用常驻cups:打印服务,服务器上几乎用不到
关闭用systemctl disable --now,比如:
bash复制systemctl disable --now abrtd cups rhsmcertd
千万别一上来就systemctl stop一堆服务,很多服务的依赖关系是隐性的,停掉会导致不明不白的故障。稳妥的办法是先systemctl list-unit-files --state=enabled看一遍所有开机自启的服务,查清楚每个服务的用途再决定去留。
另外可以用systemd-analyze critical-chain查看启动关键路径,如果某个服务卡了系统十几秒,值得单独排查它为什么慢。这步做完,开机时间通常能压掉三分之一以上。
3. 内存管理优化:别让小内存拖累了整体性能
3.1 swap与vm.swappiness的合理配置
RHEL9默认的vm.swappiness=60不算激进,但也谈不上适合服务器。这个参数控制内核回收匿名内存页面(anonymous pages)的倾向,值越大越积极使用swap。
对大多数服务器场景,我建议把swappiness降到10到30之间。原因很实在:swap读写性能远低于物理内存,宁可让内核更努力地回收文件缓存,也别频繁把进程内存页换到磁盘上。特别是数据库、Java应用这类吃内存的进程,一旦发生swap抖动,延迟呈指数级上升。
调整方法:
bash复制sysctl -w vm.swappiness=20
echo "vm.swappiness = 20" >> /etc/sysctl.d/99-tuning.conf
vm.vfs_cache_pressure也值得关注。默认100,用于控制内核回收目录项(dentry)和inode缓存的速度偏好。如果机器内存不大又跑着大量小文件操作,可以适当降到50到60,让系统更愿意保留文件元数据缓存,减少磁盘元数据读取。
3.2 透明大页THP的处理
透明大页(Transparent Huge Pages)在RHEL9系列默认是开启的,它让内核自动把连续的2MB内存映射为大页,减少TLB miss。理论很美,实际对数据库类负载非常不友好。
原因有几个方面。第一,THP的分配过程可能触发内存规整(compaction),这个过程会阻塞进程,产生几十毫秒甚至更高的延迟尖峰。第二,数据库本身往往有自己的内存管理策略,比如Oracle用共享内存、MySQL用InnoDB buffer pool,它们更希望拿到常规的4KB页,而不是被动享受THP。第三,THP在内存碎片严重时反而会增加内存浪费和回收成本。
我在线上处理MySQL、PostgreSQL实例时,无一例外都把THP调整为madvise而不是never。never意味着完全不使用,madvise允许应用通过madvise()系统调用显式申请大页——换句话说,只有明确知道自己在干嘛的程序才用大页,其他程序保持常规页。
运行中调整:
bash复制echo madvise > /sys/kernel/mm/transparent_hugepage/enabled
配合启动参数的transparent_hugepage=madvise,持久生效。怎么确认当前状态?cat /sys/kernel/mm/transparent_hugepage/enabled,方括号括起来的那个就是当前生效值。
3.3 内存回收与脏页参数详解
Linux内核的脏页(dirty pages)参数直接影响写性能。默认值适合通用场景,但针对高并发写入的应用可以进一步优化。
常用参数有两个:
vm.dirty_ratio:脏页达到系统内存的百分比后,写入进程会被阻塞强制刷盘,默认20vm.dirty_background_ratio:脏页达到这个百分比后,后台内核线程开始异步刷盘,默认10
如果应用对写延迟敏感,比如小文件高频写入、数据库日志写,可以把两个值都调低,让数据更早落盘,减少峰值阻塞。但如果追求吞吐,比如批量导入、大文件拷贝,保持默认或者调高反而更好,让脏页多攒一会儿,一次刷更多数据。
bash复制sysctl -w vm.dirty_ratio=10
sysctl -w vm.dirty_background_ratio=5
vm.min_free_kbytes也值得调一下。它控制内核保留多少空闲内存给紧急分配用。默认值偏保守,如果Redis这类应用突然需要连续大块内存,可能因为可用内存不足而卡顿。建议设置成物理内存的1%到2%左右。比如128GB内存的机器,设成1GB到2GB:
bash复制echo "vm.min_free_kbytes = 1048576" >> /etc/sysctl.d/99-tuning.conf
注意,这个参数不能随便往高了设,设得过高会导致内核在内存还很充裕时就开始回收缓存,白白浪费内存。
4. 存储与文件系统优化:把读写延迟压下来
4.1 IO调度器与挂载参数的选择
RHEL9系列对NVMe SSD和SATA SSD默认没有启用传统的IO调度器,看/sys/block/nvme0n1/queue/scheduler会发现是none。这其实是正确选择:现代SSD自己内部已经做了队列和合并优化,内核再插一层IO调度器纯属画蛇添足。
机械盘则不同,RHEL9默认使用的mq-deadline调度器能有效减少寻道时间,这是合理的。千万不要“优化”成none,机械盘加上内核调度器能显著改善多进程同时写入时的吞吐。
挂载参数方面,对数据盘我强烈建议加noatime。默认的relatime虽然比atime好,但每次访问文件还是可能触发时间戳更新,对高并发读多写少场景依然有开销。noatime直接关掉访问时间更新,性能提升在小文件读取场景非常明显。举例:
bash复制mount -o noatime,nodiratime /dev/mapper/vg_data-lv_data /data
持久化写入/etc/fstab,确保重启不丢。如果你用XFS文件系统,还可以考虑nobarrier挂载参数——XFS在RHEL9上默认用日志屏障保证数据一致性,但如果底层硬件自带掉电保护缓存(很多企业级SSD都有),关掉屏障能减少写日志时的等待。
4.2 文件系统层面的优化:XFS参数与inode策略
XFS是RHEL9默认文件系统,RHEL9.7的XFS支持reflink和基于extent的分配,性能表现很稳。但对于特定的业务模式,还是有一些调优空间。
一个是inode数量问题。创建XFS时默认按每GB空间分配固定比例的inode,如果你的业务是海量小文件(比如对象存储、日志目录),默认inode可能不够用。创建时可以用mkfs.xfs -i maxpct=10提高inode占用空间比例:
bash复制mkfs.xfs -f -i maxpct=10 /dev/mapper/vg_data-lv_data
如果你已经格式化好了才发现inode不够,那就只能重新格式化或者改用其他文件系统,所以这一步要在初始化时就想清楚。另一个是日志大小。XFS日志默认128MB,对频繁元数据操作适合调大到512MB甚至可以格到1GB:
bash复制mkfs.xfs -f -l size=512m /dev/mapper/vg_data-lv_data
日志越大,能缓冲的元数据更新就越多,突发写入时的性能越好,但日志区也吃磁盘空间,需要权衡。
4.3 日志与临时目录的优化
journald是RHEL9的默认日志系统,但它的存储策略容易让人意外踩坑。默认SystemMaxUse没有硬性限制,日志文件几乎可以无限增长,最终占满磁盘,尤其在容器环境下特别容易发生。建议做两件事:限制日志最大占用,并开启日志压缩:
bash复制mkdir -p /etc/systemd/journald.conf.d
cat > /etc/systemd/journald.conf.d/size.conf <<EOF
[Journal]
SystemMaxUse=500M
Compress=yes
EOF
systemctl restart systemd-journald
这样重启后journal目录占用超过500MB就会自动清理旧日志。注意这是全局生效,如果你希望每个服务的日志单独控制,RHEL9.7还支持按unit配置日志限额,journalctl --vacuum-size=也可以临时压缩。
临时目录方面,我一般不建议把/tmp挂到tmpfs,因为tmpfs吃内存。但如果你跑构建任务多,临时文件读写频繁,可以考虑把/tmp挂到SSD上用noatime挂载,或者在构建脚本里把临时目录指向/dev/shm这种内存盘。后面这种方案对编译类任务效果立竿见影,但要注意内存占用上限。
5. 网络协议栈优化:降低延迟与丢包
5.1 核心网络内核参数详解
网络优化是RHEL9.7优化里最容易出效果、也最容易出问题的部分。内核网络协议栈有一堆参数可以调。我举几个高频使用的,并且说一下怎么调、为什么。
bash复制# /etc/sysctl.d/99-network-tuning.conf
net.core.somaxconn = 4096
net.core.netdev_max_backlog = 16384
net.ipv4.tcp_max_syn_backlog = 8192
net.ipv4.tcp_fastopen = 3
net.ipv4.tcp_tw_reuse = 1
net.core.somaxconn控制监听队列长度。Nginx、Redis这类高并发服务的监听队列如果太小,在大流量下会出现连接丢弃。默认128对生产环境太低了,我一般调到4096甚至更高,但前提是应用本身支持高队列值(Nginx的backlog参数也要同步调)。tcp_max_syn_backlog同理,是SYN半连接队列长度,抗SYN冲击时很有用。tcp_tw_reuse让处于TIME_WAIT的连接在NAT场景下可以更快复用,对短连接海量的业务场景帮助很大。tcp_fastopen在客户端和服务端之间建立连接时少一次RTT,对大量HTTPS请求场景性能提升可观。
缓冲区方面,默认rwmem可能不够,尤其是大流量、高延迟的长肥网络:
bash复制net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
TCP拥塞控制算法也有讲究,RHEL9默认是cubic,对大多数宽带有线网络很均衡。如果你是带宽较大、丢包率较高的链路,tcp_bbr往往能拿到更高吞吐和更低延迟,加载方式:
bash复制modprobe tcp_bbr
echo "net.ipv4.tcp_congestion_control = bbr" >> /etc/sysctl.d/99-network-tuning.conf
sysctl -p /etc/sysctl.d/99-network-tuning.conf
sysctl net.ipv4.tcp_congestion_control
5.2 网卡多队列与中断绑定
sysctl调的是协议栈,网卡层面同样存在优化空间。首先用ethtool -l eth0看网卡支持几队列,RHEL9下支持多队列的网卡(比如大部分Intel ixgbe、Mellanox系列)会自动分配多个收发队列。如果看到的Combined只有1,那就要考虑打开RSS:
bash复制ethtool -L eth0 combined 8
多队列数量建议等于物理CPU核心数或略少。过多反而会因为缓存局部性变差导致性能下降。其次关注软中断分发,irqbalance服务会自动均衡中断到各个CPU,通常建议保持开启。如果用的是DPDK、VPP这类专用网络框架,需要关闭irqbalance并把网卡中断绑定到固定的几个核上,这就是AF_XDP和CPU绑定的范畴了,目前RHEL9.7的原生内核支持做得不错,值得单独开一篇研究。
还有一个常见坑:ethtool -K eth0 rx-checksumming on这类硬件卸载功能默认开着,别乱关。有些一键优化脚本会为了某些协议协商方便把网卡硬件卸载全关了,结果CPU占用飙升、吞吐下降,得不偿失。
6. 安全加固与性能的平衡:哪些优化不能做
6.1 SELinux与防火墙:别为了性能放弃安全边界
网上很多优化教程第一条就是setenforce 0,然后让你改/etc/selinux/config,理由是SELinux审计有额外开销。这话在十年前有点道理,但在RHEL9.7上完全站不住脚。
现代SELinux的策略编译和缓存机制已经很成熟,实测在大多数工作负载下开销小于3%。而RHEL9系列默认的强制模式(Enforcing)保护的是你的核心业务边界——比如Web目录被写入了木马,SELinux能阻止它被Apache执行。关掉它就是裸奔。我建议所有生产环境保持SELINUX=enforcing,如果某个应用确实和SELinux策略冲突,正确做法是调整策略或添加httpd_sys_rw_content_t这类自定义规则,而不是整体关闭。
firewalld同样不建议关闭。默认配置下它对性能的影响微乎其微,但提供了动态防火墙管理功能,比清空iptables规则安全得多。真觉得firewalld占用高,可以先检查是不是有太多复杂规则在每次连接时都被遍历。一个技巧是用nft list ruleset看一下规则集规模,如果规则条数上千且命中率高,建议调整规则顺序、把高频匹配的规则放前面,而不是关防火墙。
6.2 不要迷信“一键优化脚本”:危害与回滚策略
网上一搜RHEL9优化,满屏是“一键优化脚本”。这些脚本顺手改了一堆内核参数、关了一堆服务、塞了一堆工具,但完全不了解你的业务。我见过的翻车案例太多了:有人跑完脚本数据库变慢,有人关掉THP惹得Oracle崩溃,有人禁用irqbalance导致软中断全挤到一个核上,还有人把iptables全清空导致业务网络隔离失效。
优化原则应该是“最小必要改动 + 每次只动一类参数 + 随时可回滚”。每次修改前备份当前的sysctl配置和grub配置:
bash复制sysctl -a > /root/sysctl-before-tuning.txt
cp /etc/grub2.cfg /root/grub2.cfg.bak
然后每改一组参数,记录改了什么,为什么改,观察一两天再继续。回滚也有明确思路:
- 内核参数:
grubby --update-kernel=ALL --remove-args="xxx" - sysctl参数:删掉
/etc/sysctl.d/里新增的配置文件,重启或重载 - tuned profile:
tuned-adm profile切回去 - 服务:
systemctl enable重新启用
这套回滚思路在我手里救回过好几套环境,养成这个习惯,优化就不可怕。
7. 常见问题与排查实录
7.1 案例复盘:调优后系统变慢的三个典型场景
第一个case:某台跑ES的机器,我调低了vm.swappiness到5,结果ES启动时内存占满,发生OOM。原因是我没考虑到ES的JVM堆外内存很大,系统在内存不足时因为swap太不积极,直接把进程杀了。解决方法是把swappiness回调到20,同时给ES单独配置cgroup内存限制,而不是全局压制swap。
第二个case:一台跑Nginx反代的服务器,我调大了somaxconn到4096,但Nginx的listen指令没同步加backlog=4096,实际队列还是默认511。优化参数没有“打通”应用层,等于白改。这类问题排查方法是ss -lnt看Recv-Q是否有积压,如果Recv-Q长期非零,说明应用队列和内核参数不匹配。
第三个case:一台新部署的RHEL9.7测试环境,我把mitigations=off和THP=never加进去了,重启后应用正常运行,但内核日志出现了奇怪的报错——原来这台机器跑的是新的Intel混合架构CPU,某些安全缓解措施和混合调度有关系,关掉后某些CPU状态切换异常。这说明优化参数必须在你自己真实的硬件上验证,别人机器上验证过的不代表你的硬件没问题。
7.2 参数生效与重启后失效的排查思路
RHEL9.7优化后重启失效是个高频问题。常见原因有三。一是改动了/proc/sys下的参数但没写入/etc/sysctl.d/,进程重启或系统重启就没了。二是改了tuned profile但没确认profile确实被激活,有些场景tuned-adm会静默fallback到默认profile。三是改了grub参数但更新了内核,新内核的cmdline被grubby --update-kernel重新生成,之前追加的参数丢了。
排查顺序建议:
sysctl 参数名确认当前值cat /proc/cmdline确认启动参数tuned-adm active确认profile状态systemctl status tuned确认tuned进程是否正常运行
RHEL9.7还引入了一个新特性值得注意:某些sysctl参数可以通过systemd-sysctl动态应用,如果你用了systemd的sysctl配置文件,注意优先级规则。/etc/sysctl.d/下文件按字母序重名覆盖,数字前缀越小优先级越高。我习惯把自定义配置放在/etc/sysctl.d/99-custom.conf,避免被其他包覆盖。
7.3 常用优化参数速查表
| 参数 | 默认值(典型) | 建议值 | 适用场景 | 风险等级 |
|---|---|---|---|---|
| vm.swappiness | 60 | 10-30 | 服务器/数据库 | 低 |
| vm.vfs_cache_pressure | 100 | 50-60 | 服务器内存小 | 中 |
| vm.dirty_ratio | 20 | 10 | 高并发写入 | 中 |
| vm.dirty_background_ratio | 10 | 5 | 高并发写入 | 中 |
| vm.min_free_kbytes | 动态 | 物理内存1%-2% | 应用内存波动 | 中 |
| net.core.somaxconn | 128 | 4096 | Web/中间件 | 低 |
| net.core.netdev_max_backlog | 1000 | 16384 | 高吞吐网络 | 中 |
| net.ipv4.tcp_tw_reuse | 0 | 1 | 短连接多 | 低 |
| net.ipv4.tcp_fastopen | 1 | 3 | 高并发HTTP | 低 |
| net.ipv4.tcp_congestion_control | cubic | bbr | 大带宽高延迟 | 中 |
| io调度器(SSD) | none | none(保持) | 所有SSD | 无 |
| io调度器(机械盘) | mq-deadline | mq-deadline(保持) | 机械盘 | 无 |
各环境的硬件、业务、负载模型差异巨大,上表只是基准参考值。真正的优化要做的是理解每个参数背后的原理,在自己环境里验证,而不是直接照抄。
最后分享一点个人体会。RHEL9.7的优化不是跑一遍脚本就完事的事情,它更像一个持续观察、小步微调、验证回滚的循环。我把这套流程沉淀成了一份checklist,每次接手新环境都对照走一遍:先摸底、再定目标、逐项优化、持续监控。踩过几次坑之后明白了,最宝贵的不是那些参数命令,而是对系统行为的理解能力——知道什么时候该动参数,什么时候其实该加内存、换磁盘或者改应用代码。这个认知比任何优化脚本都值钱。
