接手一台RHEL9.7服务器,我向来不急着往里面装东西,而是先花半天时间测基线、翻配置,搞清楚系统默认状态下究竟哪里在拖后腿。很多朋友一听到“优化”就想到网上那些一键脚本、激进参数,实际上RHEL9.7作为Red Hat企业版Linux 9系列的重要更新版本,延续了基于Linux 5.14内核分支的技术路线,默认配置走的是“通用、安全、保守”的路子,能兼容绝大多数硬件和业务场景,代价就是它不会替任何特定负载做最优取舍。这篇文章记录的是我在生产环境里对RHEL9.7做性能优化的完整思路和操作片段,覆盖内核启动参数、sysctl运行时参数、systemd服务裁剪、内存与Swap策略、存储IO、网络栈、日志治理以及数据库慢SQL背后的系统瓶颈排查,适合负责服务器交付和性能调优的工程师,也适合刚接触Linux系统管理、想理解优化底层逻辑的新手。
1. 优化前先摸清家底:RHEL9.7的默认配置与性能基线
1.1 RHEL9.7默认状态与可优化组件
RHEL9.7的默认技术栈里有几个东西是必须心里有数的:内核基于5.14主线分支、默认文件系统为XFS、统一使用cgroup v2、SELinux默认强制模式、firewalld默认启用、tuned服务默认开启、systemd-oomd默认参与内存管理,容器运行时则内置了Podman。这套组合本身是经过Red Hat大量兼容性测试的,放在任何一台x86_64或ARM64服务器上都能开箱即用,但“开箱即用”不等于“贴合业务”。
默认配置为了照顾所有使用场景,会在几个方向分别让一点性能出去。比如内核定时器、电源管理、日志记录、安全审计,每一样都在消耗CPU和IO,而业务侧真正需要的往往只有其中一部分。优化这件事,本质上就是把这些通用策略逐个掰开,判断哪些保留、哪些调整、哪些关掉。
不过我必须先泼一盆冷水:有些组件是系统运行的安全底座,不建议为了性能去动。SELinux在RHEL9.7里默认强制模式,如果实在遇到兼容问题,最多临时切到permissive,千万别直接disabled;firewalld和auditd也是一样,内部网络要关闭必须走审批流程,不能拿性能当挡箭牌。我见过不止一个人把SELinux关了以后应用反而出诡异问题,因为上下文标签对不上,最后查了两天才发现是自己把安全层掀了。
1.2 性能基线的采集方法与关键指标
没有数据支撑的调优都是盲调。我会在改动任何参数之前,先跑一轮完整的性能基线和压测,把优化前的状态量化记录下来。RHEL9.7默认没有装完整的性能统计工具包,需要先补上:
bash复制dnf install -y sysstat iotop perf stress-ng fio iperf3 sysbench
systemctl enable --now sysstat
接下来至少要采集以下几类数据:
- CPU维度:
mpstat -P ALL 1,看每个核心的使用率、软中断占比、是否出现单个核心打满而其他核心空闲的情况。 - 内存维度:
free -h和vmstat 1,重点关注si、so两列。只要Swap换入换出持续不为0,说明物理内存压力已经不小。 - 存储维度:
iostat -x 1,关注%util、r_await、w_await。这里有个常见误区,%util接近100%不代表磁盘坏了,也可能是队列深度打满,要结合avgqu-sz一起看。 - 网络维度:
sar -n DEV 1和sar -n TCP,ETCP 1,看吞吐、丢包、重传。 - 调度维度:
pidstat 1,观察关键进程的CPU使用率和自愿上下文切换次数。
采集时长要覆盖业务低峰和高峰两个时段,建议至少30分钟。压测工具方面,存储用fio,网络用iperf3,CPU和内存整体压力用stress-ng,数据库场景用sysbench。优化前后必须用同样的工具、同样的参数、同样的数据量做对比,否则结论没有说服力。
提示:优化前备份也不是可选项。
cp /etc/sysctl.conf /etc/sysctl.conf.bak、cp /etc/default/grub /etc/default/grub.bak,还有/boot/grub2/grub.cfg,这些文件改动成本低,但回滚成本高。备份文件放好,半夜出问题能救命。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 内核启动参数与sysctl调优:从开机到运行时的系统级优化
2.1 GRUB启动参数优化
RHEL9.7的启动参数集中在/etc/default/grub的GRUB_CMDLINE_LINUX里,修改以后重新生成引导配置并重启才能生效。以一台数据库服务器为例,我通常会加入下面这些参数:
code复制GRUB_CMDLINE_LINUX="... rhgb quiet transparent_hugepage=never intel_idle.max_cstate=1 processor.max_cstate=1 nmi_watchdog=0"
逐个说为什么。
transparent_hugepage=never,也就是关闭透明大页。THP的本意是把小块内存合并成大页,降低TLB miss,但对数据库这类延迟敏感型应用并不友好。内核里的khugepaged线程会周期性扫描内存并尝试合并大页,这个过程会引入CPU毛刺和锁竞争,实测中经常导致延迟分布出现长尾。数据库服务器如果依赖的是大规模顺序扫描,THP收益不明显,但延迟抖动风险是实实在在的。所以保守做法就是直接never,或者用madvise模式只对显式声明的大页分配生效。
intel_idle.max_cstate=1 和 processor.max_cstate=1 限制CPU进入深度C状态。现代CPU在空闲时会自动进入低功耗状态,唤醒延迟随之增大,对于高频交易、消息队列这类要求微秒级响应的业务影响明显。把这个值设为1,相当于禁止CPU睡得太沉,换取更快的唤醒响应。注意,intel_idle针对Intel CPU,AMD平台主要靠processor.max_cstate,不要照搬全套。
nmi_watchdog=0 关闭NMI看门狗。内核默认用不可屏蔽中断周期性检查CPU是否卡死,这个周期性的中断在极端低延迟场景下会带来少量额外开销。但对绝大多数业务来说,NMI watchdog的代价可以忽略,而且它能在内核卡死时提供关键线索,我的建议是——除非你确实在做低延迟优化并且已经用perf证实它干扰了业务,否则保持默认就好。
修改完执行:
bash复制grub2-mkconfig -o /boot/grub2/grub.cfg
UEFI引导的机器路径可能不同,用grub2-mkconfig -o /boot/efi/EFI/redhat/grub.cfg。重启后检查cat /proc/cmdline确认参数是否生效。需要特别提醒:云服务器上这类参数常常不生效,因为虚拟机CPU的电源管理由宿主机控制,你在guest里怎么调都只是心理安慰,所以先看环境再决定要不要改。
2.2 sysctl运行时内核参数调优
运行时内核参数通过/etc/sysctl.conf或/etc/sysctl.d/下的文件管理。下面给出一个我常用的“通用保守版”配置,适合大多数生产服务器:
code复制vm.swappiness = 10
vm.vfs_cache_pressure = 50
vm.dirty_background_ratio = 5
vm.dirty_ratio = 10
vm.min_free_kbytes = 1048576
net.core.somaxconn = 1024
net.ipv4.tcp_max_syn_backlog = 4096
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_keepalive_time = 600
net.core.netdev_max_backlog = 4096
net.ipv4.tcp_max_tw_buckets = 8192
fs.file-max = 1048576
逐个讲背后的逻辑。
vm.swappiness = 10。默认值通常是30,含义是内核在回收内存时对Swap的倾向程度。很多优化文章教人直接设0,我不建议。RHEL9.7的内核版本里,swapiness=0已经不是“绝对不Swap”,而是“尽最大可能避免”,但紧急情况下依然会换出。设成10的意义在于:尽量保留文件页缓存,同时给突发内存压力留一条退路。直接设0一旦碰上内存尖峰,系统可能直接进入OOM流程,反而更危险。
vm.vfs_cache_pressure = 50。这是控制内核回收目录项和inode缓存的倾向性,默认100偏激进,设成50会让内核更愿意把这些缓存留在内存里。对文件数量很大的业务,这个参数体感很明显——ls、stat这类操作会顺畅很多。
vm.dirty_background_ratio = 5、vm.dirty_ratio = 10。这两个参数控制脏页写回策略。简单说,后台进程在脏页达到5%时开始写回,同步阻塞阈值在10%。如果业务是大量小文件写入或数据库日志写入,把这两个值调低能避免脏页积压到某一刻集中刷盘造成的IO尖峰。但注意,太低的dirty_ratio会频繁触发写回,反而增加IO次数,得不偿失。
vm.min_free_kbytes = 1048576。这个参数比较敏感,它告诉内核要保留多少空闲内存用于紧急分配。设置太低,内存碎片化严重时大块分配会失败;设置太高,等于白白冻结内存。常见保守做法是物理内存的1%左右,64GB机器给1GB是合理的起点。我见过有人把128GB机器的min_free_kbytes设成4GB,结果可用内存少了3GB,业务却没因此变快,纯属浪费。
网络参数部分,somaxconn和tcp_max_syn_backlog影响连接队列深度。高并发短连接场景下,默认值容易丢连接,调高能缓解SYN重传。但队列也是要占内存的,别调到几十万,一般几千到一两万足够了。tcp_fin_timeout缩短TIME_WAIT的等待时间,配合tcp_max_tw_buckets控制TIME_WAIT连接总数,对高并发的短连接服务有明显帮助。
配置写好后执行sysctl -p加载,再用sysctl vm.swappiness net.core.somaxconn确认生效值。有一点要记住:sysctl.conf里的参数不是越多越好,内核默认值经过社区大规模验证,你对业务的判断未必比内核更准确。每填一个参数都要能说清楚“它在保护什么、牺牲什么”。
3. 服务裁剪与资源管理:让systemd为业务让路
3.1 关闭无用服务与开机自启项
RHEL9.7最小化安装已经相当干净,但如果你装的是带GUI的版本,桌面相关服务会带来明显多余开销。第一步先看看当前开机自启了哪些服务:
bash复制systemctl list-unit-files --type=service --state=enabled
常见可以关闭的服务包括:cups(打印服务,服务器上基本用不到)、bluetooth(蓝牙栈)、avahi-daemon(mDNS广播)、abrtd(崩溃报告)、ModemManager(移动宽带管理)、mdmonitor(没有做软RAID就没必要跑)。关闭命令:
bash复制systemctl disable --now cups.service avahi-daemon.service
systemctl mask cups.service
disable和mask的区别值得说清楚。disable只是移除开机自启项,但如果某个依赖关系把它拉起来,它照样会运行;mask则是把服务单元彻底符号链接到/dev/null,任何依赖都无法启动它。对于确定不要的服务,直接mask比disable更彻底。
但mask是有风险的,千万别mask核心服务,比如systemd-journald、systemd-logind、dbus、NetworkManager、sshd、tuned、chronyd、firewalld、auditd。有一回我想优化一个内部测试环境,顺手mask了systemd-journald,结果系统日志全丢,排查问题跟盲人摸象一样,最后只能重启恢复。教训很深刻:核心服务可以调配置,但不要彻底摁死。
开机耗时分析可以用systemd-analyze blame和systemd-analyze critical-chain,前者看每个单元的启动耗时,后者看关键路径。如果你发现某个服务稳定占用几百毫秒,而且业务完全不需要,再决定disable或mask。
3.2 tuned、systemd-oomd与内存回收策略
RHEL9.7的tuned是个好东西,它是Red Hat官方提供的系统调优框架,把一堆零散的内核参数、电源管理设置、磁盘参数封装成预置profile。先看当前状态:
bash复制tuned-adm active
tuned-adm recommend
一般服务器我会推荐throughput-performance,这个profile会关闭省电模式的频率调节,把CPU governor设为性能模式,并调整一批存储和网络参数,适合绝大多数业务。数据库、消息队列这类延迟敏感型应用,可以切到latency-performance,它更进一步关掉电源管理对延迟的影响。如果这台机器是KVM虚拟化宿主机,virtual-host更合适,它能兼顾虚拟化场景的CPU和IO调度。
tuned也支持自定义profile,这个能力非常实用。比如我想在latency-performance基础上叠加自己的sysctl设置:
code复制cd /etc/tuned/
mkdir myprofile
vim myprofile/tuned.conf
文件内容:
code复制[main]
summary=My database optimized profile
include=latency-performance
[sysctl]
vm.swappiness=10
vm.dirty_ratio=10
[disk]
readahead=256
然后tuned-adm profile myprofile切换。自定义profile放在/etc/tuned下,系统升级不会被覆盖,这点很让人安心。
再聊systemd-oomd。RHEL9.7默认启用了systemd-oomd,它监控cgroup的内存压力,在内存压力过高时主动杀掉一些进程来避免整机卡死。这个机制在容器场景很有用,不需要等内核OOM killer随机选目标。但默认阈值不一定适合所有业务,可以通过配置文件调整:
code复制vim /etc/systemd/oomd.conf
示例:
code复制[OOM]
SwapUsedLimit=90%
MemoryPressureLimit=80%
如果内存压力频繁触发,而进程频繁被杀,说明要么物理内存确实不够,要么阈值太激进。不要无脑关掉systemd-oomd,而是先结合journalctl -u systemd-oomd看它的决策日志,再决定调整方向。
最后说Swap策略。网上很多文章教你“生产环境直接关Swap”,我的态度是:谨慎。Swap存在是有价值的,它给内存尖峰留了缓冲,代价是换页时IO延迟升高。如果你确信物理内存余量充足,可以通过swapoff -a && swapon -a临时清空Swap,但别把这个写进开机脚本。更好的方案是维持少量Swap,加上swappiness设为10——既能兜底,又不会频繁触发换页。
4. 存储与文件系统优化:让磁盘读写不再拖后腿
4.1 XFS挂载参数与IO调度器选择
RHEL9.7默认文件系统XFS,整体设计偏向大文件和高吞吐,但挂载参数走的是通用默认值,针对业务场景还可以更细。最常用的挂载优化是加noatime和nodiratime。系统默认会在文件被读取时更新atime(访问时间),这在高频率读取的场景下会产生大量元数据写入。我维护的Web静态文件服务器,光加这两个参数,磁盘写IO直接降了一截,效果非常直观。
如果是大文件顺序读写的存储服务器,可以加allocsize=1m,让XFS预分配大块空间,减少文件碎片。如果是小文件随机写的数据库场景,这个参数反而可能有副作用,分配粒度太大会浪费空间和IO。所以fstab配置要根据业务来:
code复制UUID=xxxx /data xfs defaults,noatime,nodiratime,allocsize=1m 0 0
改完mount -o remount /data验证,再用mount | grep /data确认参数已生效。
IO调度器也是存储优化的重点。RHEL9.7采用多队列块层,查看当前调度器:
bash复制cat /sys/block/sda/queue/scheduler
对于NVMe固态硬盘,none(也就是NOOP)是最合适的,因为设备本身支持内部队列和调度,内核层再去排序只是白费CPU。对于机械硬盘,mq-deadline能保证请求的延迟上限,避免某个队列饿死。判断规则很简单:SSD用none,HDD用mq-deadline。如果你的系统盘和数据盘混在一起,还需要区分设备来配置。用udev规则固定:
code复制vim /etc/udev/rules.d/60-iosched.rules
内容:
code复制ACTION=="add|change", KERNEL=="sda", ATTR{queue/scheduler}="none"
ACTION=="add|change", KERNEL=="sdb", ATTR{queue/scheduler}="mq-deadline"
然后:
bash复制udevadm control --reload-rules
udevadm trigger
这里有个很容易踩的坑:/sys/block/下的调度器名字在NVMe设备上是none,在老一些的文档里写成noop,如果echo一个不存在的调度器名,系统直接返回错误。以cat /sys/block/.../queue/scheduler显示的结果为准,不要照抄网上的老命令。
队列深度和预读值也是可以调的。/sys/block/sda/queue/nr_requests默认值往往偏保守,调高可以提升极限吞吐,但会占用更多内存;read_ahead_kb对顺序读有帮助,对随机读反而是累赘,因为预读了用不上的数据等于白白占带宽。每次调整后用fio验证,不要凭感觉:
bash复制fio --name=randread --ioengine=libaio --direct=1 --rw=randread --bs=4k --numjobs=8 --iodepth=32 --runtime=60 --time_based --group_reporting
重点看IOPS和平均时延,对比调整前后的数据再决定保留还是回滚。
4.2 慢SQL优化与系统资源瓶颈排查
热搜词里有一堆“慢SQL优化”,但在RHEL9.7系统层面谈慢SQL,思路和DBA改SQL语句不太一样。应用层报数据库变慢,执行计划确实要查,但系统侧的几个指标经常被忽略,而它们往往才是压垮数据库的最后一根稻草。
排查慢SQL时,我会按这个顺序看:
iostat -x 1:看r_await和w_await。如果读延迟明显高于磁盘标称值,说明IO路径排队严重,SQL再优化也很难快起来。这时候要看是不是文件系统缓存命中率太低,或者磁盘本身性能撑不住了。vmstat 1:看si、so。数据库进程如果分页频繁,说明内存压力巨大,SQL执行计划再完美也会被换页拖慢。mpstat -P ALL 1:看是不是某个核心被打满。并行SQL场景经常出现这种情况——一条查询开几十个并行线程,把CPU核心全部占满,其他业务全部陪跑。pidstat -d 1:跟踪数据库进程的读写延迟,确认瓶颈在进程内还是进程外。
如果确认是并行SQL把CPU吃光,系统侧的解法是限制资源配额,而不是跟业务方扯皮。RHEL9.7用cgroup v2做资源限制很顺手,用systemd-run可以直接起一个受CPU配额约束的数据库进程:
bash复制systemd-run --unit=limitdb --scope --property=CPUQuota=400% --property=MemoryMax=32G -- /usr/sbin/mysqld
CPUQuota=400%表示最多使用4个核心。这样并行查询再猛,也只能在配额内折腾,不会把整台机器拖垮。当然,这种限制要跟业务方对齐阈值,不能拍脑袋定一个值。
需要说清楚的是,系统优化是给数据库“铺路”,它解决的是CPU争抢、IO排队、内存换页这些基础设施问题。SQL本身没有索引、join写成一团乱麻,你再怎么调系统参数也救不回来。但反过来,如果基础设施一堆毛病,SQL优化得再漂亮,跑起来一样慢吞吞。
5. 网络栈与容器场景优化:面向大规模并发与云原生
5.1 TCP/IP栈与网卡队列优化
网络优化对RHEL9.7来说优先级很高,尤其是高并发Web、网关、消息队列这类场景。优化的核心目标是:减少中断开销、加大队列容量、避免丢包重传。
现代网卡都支持多队列,每个队列可以由不同的CPU核心处理中断,这能极大缓解单核瓶颈。先查:
bash复制ethtool -l eth0
如果Combined队列数少于CPU核数,可以尝试:
bash复制ethtool -L eth0 combined 8
这里注意,具体支持多少队列取决于网卡驱动和硬件能力,强行设一个过高的值不会生效。如果队列数无法提升,还可以用RPS(Receive Packet Steering)把收包软中断分散到多个CPU核。RPS的配置在/sys/class/net/eth0/queues/rx-0/rps_cpus,写入的值是CPU位图。比如8核机器想用前4个核处理收包,就写入0000000f:
bash复制echo "0000000f" > /sys/class/net/eth0/queues/rx-0/rps_cpus
RPS不是开得越大越好,它会把软中断处理分摊到更多核心,但也可能因为跨NUMA访问内存反而变慢。对多数场景,分摊到同一NUMA节点内的核心就够了。
中断绑定的细节操作我不建议大家上来就做。irqbalance服务默认开启,它能让中断分布相对均衡,对一般业务完全够用。只有当你实在需要极低延迟、且已经用mpstat发现软中断集中在一个核心造成瓶颈时,才考虑停掉irqbalance手动绑核。
TCP栈的sysctl参数,前面已经给过一部分,这里补充一个容易踩坑的点:网上流传的tcp_tw_reuse优化文章非常多,但这几年的内核已经改变了它的默认语义。在RHEL9.7上不要拿老参数直接套,先cat /proc/sys/net/ipv4/tcp_tw_reuse看看默认值,再结合自己对TIME_WAIT状态的理解做判断。高并发短连接服务调整TIME_WAIT相关的参数可能有用,但改错了会导致连接复用异常,反而制造新问题。
BBR拥塞控制算法也值得提一下。如果内核有tcp_bbr模块:
bash复制modprobe tcp_bbr
echo bbr > /proc/sys/net/ipv4/tcp_congestion_control
BBR在高带宽、高延迟的跨地域传输场景能显著提升吞吐,但内网低延迟环境收益有限。想持久化配置,需要把tcp_bbr写进/etc/modules-load.d/,再把net.ipv4.tcp_congestion_control=bbr写进/etc/sysctl.conf。这个改动对网络行为影响较大,建议先在测试环境跑一遍业务流量。
5.2 Podman与cgroup资源限制、日志容量管控
RHEL9.7内置Podman,cgroup v2做资源限制比老版本顺畅得多。容器场景最容易出现的问题是“吵闹邻居”——一个容器把宿主机资源吃干榨净,其他容器全部遭殃。解决思路是每个容器都带上资源上限:
bash复制podman run -d --name web --cpus=4 --memory=8g --memory-swap=8g nginx
--memory-swap=8g和--memory=8g保持一致,表示容器内不允许使用Swap。这样做的好处是防止容器把宿主机Swap耗尽,坏处是容器内存到顶会直接OOM。具体要不要限制Swap,看业务的容忍度。用podman stats持续监控各容器的资源消耗,如果某个容器长期逼近上限,说明它的资源配置不合理,而不是资源限制本身的问题。
演进到大规模容器平台,systemd-oomd的配置就很重要了。容器多了以后,内存压力波动会很频繁,如果systemd-oomd阈值太敏感,可能出现批量杀容器的事故。查看journalctl -u systemd-oomd能找出决策依据,再根据业务容器的内存画像调整阈值。
镜像包体优化这个话题,跟“unity包体优化”“win11优化”这类热搜词本质上是同一类思路——减去一切不需要的东西。容器镜像建议做多阶段构建,把编译环境和运行环境分离,只保留运行所需的最小文件集:
dockerfile复制FROM golang:1.22 AS build
WORKDIR /src
COPY . .
RUN CGO_ENABLED=0 go build -o app .
FROM registry.access.redhat.com/ubi9/ubi-minimal
COPY --from=build /src/app /usr/local/bin/app
ENTRYPOINT ["/usr/local/bin/app"]
ubi-minimal这个基础镜像比我见过的好多自定义精简镜像还可靠,该有的证书、时区、glibc组件都在,体积却小很多。
日志治理是RHEL9.7运维里最容易被忽视的一块。systemd-journald默认对日志大小限制在配置文件中并没有写死,它会根据文件系统大小自动推导,结果就是无限增长,直到磁盘告警。我的做法是明确限制:
code复制vim /etc/systemd/journald.conf
关键配置:
code复制SystemMaxUse=500M
SystemMaxFileSize=100M
MaxRetentionSec=1month
然后重启:
bash复制systemctl restart systemd-journald
journalctl --vacuum-size=200M
这里有个操作禁忌:不要直接rm -rf /var/log/journal/下面的文件,journal的索引文件有内在关联,手动删除容易损坏日志索引,导致后续查询全部报错。用journalctl --vacuum-size是安全做法,它会按索引正确清理。
6. 常见问题排查与避坑实录
6.1 常见问题速查表
在多次RHEL9.7优化实践中,下面几个问题出现频率最高,整理成表格方便直接对照。
| 问题现象 | 可能原因 | 排查命令 | 解决办法 |
|---|---|---|---|
| 改了sysctl后性能反而下降 | 参数与业务负载不匹配,比如dirty_ratio太低导致写回频繁 | 对比优化前后iostat、perf数据 | 回滚sysctl配置,逐项验证后再重新调整 |
| fstab写错导致系统无法启动 | 挂载参数错误或设备UUID不存在 | 启动时进入emergency模式 | 用mount -o remount,rw /重新挂载根文件系统,修正fstab后umount恢复 |
| 关闭THP后Java应用启动变慢 | 某些JVM版本默认依赖大页分配 | grep Huge /proc/meminfo |
改用transparent_hugepage=madvise,让显式声明大页的分配仍然生效 |
| systemd-oomd频繁杀容器 | 内存压力阈值过于敏感 | journalctl -u systemd-oomd |
调高MemoryPressureLimit阈值,或增加物理内存 |
| 网卡RPS配置重启后失效 | 位图写入是运行时的,非持久化 | 重启后cat /sys/class/net/eth0/queues/rx-0/rps_cpus |
用systemd tmpfiles或udev规则固化配置 |
| journald日志持续占用磁盘 | SystemMaxUse未配置,大小由系统推导 | journalctl --disk-usage |
设置SystemMaxUse和MaxRetentionSec,用vacuum-size清理 |
这里重点说下fstab回滚。如果你改了/etc/fstab导致开机进入emergency模式,先执行:
bash复制mount -o remount,rw /
让根文件系统可写,然后编辑fstab把刚才加错的挂载项注释掉或改回默认值。完成后reboot。整个过程不要慌,fstab只要没删改核心的根分区和boot分区,通常都能救回来。
6.2 从实测经验出发的避坑清单
优化这件事做得越多,越觉得“少即是多”。我给自己定过几条规矩,现在基本成了团队交付的检查项:
第一,一次只调一个域。今天调内核参数,就只动内核参数,压测完记录结果再碰文件系统。同时改十个参数,性能下降了根本说不清是谁的锅。我吃亏最多的一次,是同时调了vm参数和磁盘调度器,第二天业务报慢,回滚的时候把我自己都绕晕了。
第二,所有改动都要能回答“它在保护什么、牺牲什么”。比如vm.min_free_kbytes保护的是紧急内存分配能力,牺牲的是可用内存;intel_idle.max_cstate=1保护的是唤醒延迟,牺牲的是省电。如果一句话说不出这个权衡,那这个参数就别改。
第三,云上虚拟机先测后调。很多内核参数在虚拟机里根本不生效,比如C状态、CPU频率调节、网卡多队列,这些由宿主机决定。你花半天时间调整的参数,实际上是无效功。我在云主机上做优化时,第一步永远是看/proc/cmdline和lscpu,确认哪些参数真的暴露给了guest。
第四,安全底线不要碰。优化性能不能以关闭SELinux、firewalld、auditd为代价。如果实在碰到性能与安全的冲突,优先找应用层面的替换方案,而不是掀掉系统底座。
最后说句掏心窝的话。我做过不少RHEL9.7的交付项目,印象最深的不是哪条参数帮我提升了多少性能,而是有一次只调了vm.dirty_ratio就导致数据库checkpoint频繁卡顿,最后全部回滚才恢复。从那以后我给自己定了规矩:所有优化必须能回答一个问题——它到底在保护什么、牺牲什么。RHEL9.7这套系统本身足够可靠,优化的本质是去掉默认配置中与业务无关的冗余,而不是把系统当成赛车去改装。务实一点,收益会扎实得多。
