简介:面向Linux运维工程师与服务器管理员的性能调优专题文档,聚焦网络协议栈与磁盘子系统两大核心优化方向。内容涵盖TCP内核参数调整(syncookies防SYN攻击、窗口缩放、收发缓冲区设置)、/etc/sysctl.conf持久化配置方法与生效验证,以及磁盘I/O优化策略(noatime挂载选项、hdparm硬件检测与基准测试、文件系统选型与RAID/SSD权衡)。此外还整理了/proc/sys/net下TCP/IP调优参数的说明,帮助读者理解各项配置的实际作用。文档以docx格式呈现,共1个文件,压缩包约51KB,内容紧凑、便于查阅与打印。目前已服务216人学习下载。读者可从中获得从内核参数到硬件层面的完整调优思路、可直接参考的配置示例与操作命令,适合希望提升服务器吞吐量、降低响应延迟的初中级运维人员快速落地实践。
1. Linux 性能调优这份清单:从网络到磁盘的实战参数合集
拿到这份《LINUX性能调优方法总结.docx》的时候,我第一反应是:终于有人把散落在各篇博客里的 sysctl 参数、ulimit 限制、hdparm 命令整成一份能直接抄的清单了。文档不厚,但覆盖了 TCP/IP 网络、磁盘、文件描述符、虚拟内存和网络安全五个层面,每个参数都带着「为什么改、改多大、怎么生效」的解释,不是那种只丢命令不给理由的笔记。适合两类人:一是刚接手 Linux 服务器、想系统性做一次体检调优的运维,二是在压测或者线上故障排查中需要快速定位瓶颈、又不想翻几十个网页的开发者。它解决的核心问题是:参数太多记不住,而且很多默认值对高并发场景确实偏保守,照着这份文档走一遍,能少踩不少坑。
2. TCP/IP 网络调优:sysctl 参数怎么设、为什么这么设
2.1 内核参数落盘的方式:/proc 与 sysctl.conf 的关系
所有 TCP/IP 调优参数最终都要写到 /proc/sys/net/ 目录下对应的虚拟文件里,但直接 echo 进去的问题在于重启即丢。文档里强调了一个关键点:/proc 下所有内容都是临时性的,重启动系统后任何修改都会丢失。所以正确的做法是写入 /etc/sysctl.conf,再用sysctl -p让它立即生效。
常见的做法是把参数集中写在 sysctl.conf 的末尾,用分组注释隔开。注意文档中有一个细节:net.ipv4.tcp_window_scaling: = 1这个写法冒号后面多了个空格,实际执行时会报语法错误,正确写法是net.ipv4.tcp_window_scaling = 1。我处理这类文件时习惯先cp /etc/sysctl.conf /etc/sysctl.conf.bak备份一份,改完再sysctl -p,输出里如果出现Unknown key或者invalid syntax,就知道是哪一行出了问题。
cp /etc/sysctl.conf /etc/sysctl.conf.bak # 编辑后执行,让配置立即生效 sysctl -p /etc/sysctl.conf这里sysctl -p不接路径时默认读取 /etc/sysctl.conf,如果你改了别的文件,用sysctl -p /path/to/your.conf指定。执行后屏幕上会逐行回显参数名和值,建议扫一眼有没有报错。如果只想临时测试某个参数而不想落盘,可以直接sysctl -w net.ipv4.tcp_syncookies=1,但要记住重启后失效。
2.2 SYN cookie、窗口缩放与缓冲区:三个先改的参数
文档第一个给出的是net.ipv4.tcp_syncookies = 1。它解决的是 SYN flood 攻击导致的内存耗尽问题:正常 TCP 握手是 SYN → SYN-ACK → ACK,攻击者只发 SYN 不发 ACK,半开连接堆满队列,服务器内存被拖垮。SYN cookie 不分配半开连接条目,而是把连接状态编码进 SYN-ACK 的序列号里,合法客户端返回 ACK 时再重建条目。大多数发行版默认开启,但文档明确说「确保配置这个特性更可靠」,尤其对公网服务器。
第二个是net.ipv4.tcp_window_scaling = 1。TCP 窗口决定了一次能发送多少数据而不用等确认,默认最大 64KB,如果客户端延迟大(比如跨地域),这个值就限制了吞吐。窗口伸缩在 TCP 头里启用更多位来扩大窗口,对高带宽长链路(即所谓的 BDP 大的场景)提升明显。
# 激进一点的公网服务器配置 net.ipv4.tcp_syncookies = 1 net.ipv4.tcp_window_scaling = 1 net.core.rmem_max = 16777216 net.core.wmem_max = 16777216 net.ipv4.tcp_rmem = 4096 87380 16777216 net.ipv4.tcp_wmem = 4096 65536 16777216 net.ipv4.ip_local_port_range = 1024 65000这段组合里,rmem_max 和 wmem_max 是 socket 接收/发送缓冲区的上限,tcp_rmem 和 tcp_wmem 是三个值:最小值、默认值、最大值。内核会自动在这三个值之间调节,最小值防止内存被压得过低,最大值给突发流量留空间。最后一个 ip_local_port_range 把本地可用端口从默认的 32768-61000 扩到 1024-65000,直接提升了单机同时连接数的上限。注意 1024 以下端口有约定用途,但这个值的下限设成 1024 是安全的,因为普通用户进程默认绑定不了特权端口。
2.3 TIME_WAIT 复用与 keepalive:连接数上去之后的参数
连接数大到一定程度,TIME_WAIT 状态会积压大量 socket。文档给的两个参数是net.ipv4.tcp_tw_reuse = 1和net.ipv4.tcp_tw_recycle = 1,前者允许新连接复用 TIME_WAIT 阶段的端口,后者加快 TIME_WAIT 状态的回收。这里有个重要边界:tcp_tw_recycle 在 NAT 环境下会有严重的兼容性问题,因为它是基于源 IP 做时间戳匹配的,NAT 后面多台机器共用同一个外网 IP,时间戳不一致会导致连接被丢弃。我在生产环境里只开 tcp_tw_reuse,不开 recycle,靠 tcp_fin_timeout=30 来控制等待时间。文档里给的net.ipv4.tcp_fin_timeout=30是把 FIN-WAIT-2 的保持时间从默认 60 秒缩短到 30 秒,让内存更快释放。
# 连接数大的 Web 服务器推荐组合 net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_fin_timeout = 30 net.ipv4.tcp_keepalive_time = 1800tcp_keepalive_time 默认 7200 秒,也就是 2 小时才探测一次死连接,改成 1800 秒能更快发现并清理那些建了连接但不再传数据的客户端。对 Web 服务器来说,这个调整能显著降低内存占用。文档还提到net.ipv4.tcp_max_syn_backlog = 4096,这是半连接队列的最大长度,默认 1024,并发高且上游有拨号这类高延迟客户端时容易溢出,调大能缓解 SYN flood 造成的丢连接问题。但要注意,backlog 队列长度还受应用的 listen backlog 限制,比如 Nginx 的 backlog 参数,两层要一起调才有效。
2.4 安全相关的网络参数:顺手一起做的加固
文档后半部分列了一组安全类 sysctl,虽然定位是安全加固,但对性能也有间接作用。比如net.ipv4.conf.all.accept_source_route = 0关闭源路由,防止黑客伪造 IP 路径;net.ipv4.conf.all.accept_redirects = 0和net.ipv4.conf.all.send_redirects = 0关闭 ICMP 重定向,避免路由被污染。还有一个容易漏掉的:net.ipv4.icmp_echo_ignore_broadcasts = 1,不响应广播 ping,防止被用来做 smurf 攻击放大。
# 安全加固又不影响性能的参数 net.ipv4.conf.all.accept_source_route = 0 net.ipv4.conf.all.accept_redirects = 0 net.ipv4.conf.all.send_redirects = 0 net.ipv4.icmp_echo_ignore_broadcasts = 1 net.ipv4.icmp_ignore_bogus_error_responses = 1 net.ipv4.conf.all.rp_filter = 1这里的 rp_filter 是反向路径过滤,检查数据包的源地址是否从正确的网卡进来,能防止 IP 欺骗。对 NFS 和 Samba 这类服务,文档还给了 ipfrag 相关参数,设置 IP 碎片重组的内存范围,比如net.ipv4.ipfrag_low_thresh = 262144和net.ipv4.ipfrag_high_thresh = 393216,单位是字节,表示碎片缓冲区在 256MB 到 384MB 之间浮动。这个值不要大于物理内存的一半,否则可能挤压其他内核结构。
3. 磁盘与文件子系统:从 noatime 到 ulimit 的一整套落地配置
3.1 禁用 atime:一个挂载参数省掉大量磁盘写
atime 是每次访问文件时更新「最后访问时间」的机制。问题在于,很多人只是读文件,并不关心它上次是什么时候被读的,但文件系统每次读都要写一次时间戳,在多读场景下等于每一次读操作都带了一次写放大。文档给的做法是在 /etc/fstab 的挂载选项里加 noatime。
# /etc/fstab 示例 /dev/VolGroup00/LogVol00 / ext3 defaults,noatime 1 1 LABEL=/boot /boot ext3 defaults,noatime 1 2改完 fstab 后要重新挂载才生效,对根分区这类不能直接 umount 的,可以用mount -o remount /重新挂载。文档里给的是 ext3 的写法,ext4 也适用。需要注意,像 mutt 这类邮件客户端依赖 atime 判断新邮件,如果你跑这类应用,就用relatime替代 noatime——它在 atime 大于 mtime 或 ctime 时才更新,兼顾性能和应用需求。
3.2 hdparm:IDE 磁盘的访问模式调整
对 IDE 磁盘,文档建议用 hdparm 调整访问方式,核心是确认开启 DMA 和 32 位 I/O,以及设置每中断多扇区模式。第一步应该先测当前性能,hdparm -t /dev/hda就是测缓冲读取速度,要在系统空闲时跑才有参考价值。
# 查询当前设置和磁盘支持的设置 hdparm -vI /dev/hda # 启用 32 位 I/O hdparm -c 1 /dev/hda # 启用 DMA 并设置传输模式 hdparm -d 1 -X 66 /dev/hda这里 -X 后面的数字对应不同的 UDMA 模式,具体要看 hdparm 手册页。关键坑在于:hdparm 的设置重启后全部丢失,而且对 SCSI 和虚拟化磁盘基本无效。文档明确写了 SCSI 系统依赖驱动,必须把有用设置写进 /etc/rc.local。另外,云服务器和虚拟机里跑 hdparm -t 测的是宿主机虚拟磁盘的性能,参数设了也不起作用,这类环境直接跳过这步。
3.3 文件描述符与进程数:ulimit 的两层配置
ulimit 有两层:shell 会话层的ulimit -n 4096是临时的,logout 就失效;永久生效要写 /etc/security/limits.conf。文档先展示了临时命令,再补了持久化方案。
# 临时修改 ulimit -u 10000 ulimit -n 4096 ulimit -d unlimited ulimit -s unlimited ulimit -v unlimited # 永久修改:/etc/security/limits.conf * soft noproc 11000 * hard noproc 11000 * soft nofile 4100 * hard nofile 4100soft 是软限制,进程可以自行调高但不超过 hard 限制;hard 是硬限制,只有 root 能提高。*表示所有用户,noproc 是最大进程数,nofile 是最大打开文件数。文档还补了一个关键配置:/etc/pam.d/login 里要有一行session required /lib/security/pam_limits.so,否则 limits.conf 不生效。这是很多新手调了 limits.conf 却毫无反应的隐性原因。
3.4 /etc/profile 与 rc.local:两处启动脚本的配合
文档把 ulimit 同时写进了 /etc/profile 和 /etc/rc.d/rc.local,两者作用不一样。/etc/profile 在每次用户登录时加载,影响交互式 shell;rc.local 在系统启动末尾被执行,适合放需要 root 权限的 echo 命令和 hdparm 设置。
# /etc/profile 追加 ulimit -u 10000 ulimit -n 4096 ulimit -d unlimited ulimit -s unlimited ulimit -t unlimited ulimit -v unlimited# /etc/rc.d/rc.local 追加(需确保有执行权限) echo 131072 > /proc/sys/fs/file-max echo 131072 > /proc/sys/fs/inode-maxfile-max 是系统级最大文件句柄数,inode-max 是系统级 inode 上限。文档给出了按内存换算的参考:1G 内存设 65535,2G 设 131072,4G 设 262144。注意 rc.local 在 systemd 环境里要chmod +x /etc/rc.d/rc.local并且在文件头部加上#!/bin/bash,否则里面写的命令不执行,这也是一个常见的翻车点。
4. 内存与虚拟内存调优:dirty buffer 和 kswapd 的取舍
4.1 vm.bdflush:九个参数只动三个
文档对虚拟内存调优的态度很审慎:一次只改一个参数,然后监测效果。vm.bdflush 有 9 个参数,但文档建议只动其中 3 个:nfract 是 bdflush 后台进程允许缓冲区变 dirty 的最大百分比,ndirty 是立即写盘的缓冲区数量,nfract_sync 是同步前允许 dirty 的最大百分比。
sysctl -w vm.bdflush="30 500 0 0 500 3000 60 20 0"这组值按文档原样给出,对应的是:nfract=30(缓冲区 30% dirty 时触发写回),ndirty=500(每次最多写 500 个缓冲区),nfract_sync=60。这里要提醒一句:现代内核(2.6.32 之后)已经用 vm.dirty_ratio 和 vm.dirty_background_ratio 替代了 bdflush,老参数写进去不报错也不起作用。如果你用的是 CentOS 7 或更新的系统,应该看的是 dirty_background_ratio 和 dirty_ratio:
# 新内核推荐看这两个 net.ipv4.tcp_keepalive_time 已改 sysctl vm.dirty_background_ratio sysctl vm.dirty_ratiodirty_background_ratio 是脏页占物理内存的百分比,达到这个值后台开始写;dirty_ratio 是脏页达到这个比例时,写操作本身开始阻塞。对数据库服务器,可以适当调小 background_ratio 让写回更平缓,避免周期性 I/O 尖峰。
4.2 kswapd 与 swap 行为:三个参数控制内存页回收
vm.kswapd 同样有三个参数:tries_base、tries_min 和 swap_cluster。tries_base 相当于内核每次扫描页数量的四倍,交换频繁的系统调大它能改善效率;swap_cluster 是 kswapd 每次写入交换空间的页数,数值小对磁盘 I/O 更友好,数值大可能加重请求队列负担。
sysctl -w vm.kswapd="1024 32 64"文档在内存部分列了 vm.buffermem、vm.freepages、vm.overcommit_memory 等其他可调参数,但没有给出推荐值。overcommit_memory 值得单独说:默认 0 表示内核做启发式过度分配,数据库类应用常建议改成 1(总是 overcommit,适合 Redis 这类需要大内存映射的场景)或者 2(禁止 overcommit,严格限制)。改这个参数前务必用free -m看清楚系统内存余量,改 2 之后某些应用可能直接 OOM。
4.3 用 vmstat 验证内存调整:别靠感觉调参
文档反复强调内存调优要「使用 vmstat 检查对性能的影响」。vmstat 看三列就够了:r 是运行队列,si 和 so 是 swap in/out。si、so 长时间不为 0,说明内存真的不够,这时调 kswapd 只是缓解症状,加内存才是正解。
# 每 2 秒刷新一次,共输出 10 次 vmstat 2 10跑完看 si/so 的趋势:如果数值持续走高,说明频繁换页,调 vm.kswapd 的参数同时观察变化;如果 si/so 本来就接近 0,说明内存充足,再去调这些参数属于没事找事。文档那句「每次只改变一个参数然后监测效果」,是内存调优最容易忽略的纪律。我一般会在改之前先抓一份 vmstat 基线,改完每隔 5 分钟再抓一次,对比三组数据才决定要不要保留。
4.4 MySQL 场景下的内存参数落地
文档没有专门讲数据库,但摘要和热搜词都出现了 mysql 性能调优。结合上面的参数,MySQL 实例多或 buffer pool 大的机器上,要注意两点:vm.dirty_background_ratio 建议调小到 5-10,避免 InnoDB 刷脏页和内核 pdflush 同时抢 I/O;overcommit_memory 保持默认 0 即可,MySQL 不像 Redis 那样需要 mmap 大块虚拟地址。
另外文档在网络部分给的 rmem/wmem 默认值 256960 到 8388608 之间跨度很大,对小规格的 MySQL 服务器,建议从 87380 这个默认值开始跑,配合performance_schema观察网络等待事件再决定是否加大,一上来就设 16MB 反而可能浪费内存。
5. 常见问题与避坑记录:五条真实踩坑与排查路径
5.1 sysctl -p 报错或参数没生效
现象:执行sysctl -p后某一行提示sysctl: cannot stat /proc/sys/net/ipv4/tcp_window_scaling: No such file or directory,或者明明在配置里写了参数,cat /proc/sys/net/ipv4/tcp_window_scaling看到的值还是 0。
原因:最常见的是键名写错,比如文档里的net.ipv4.tcp_window_scaling: = 1冒号后面多了空格,或者net.ipv4.tcp.wmem写成了点和下划线混用。另一个原因是某些参数只在特定内核模块加载后才出现,比如 rp_filter 相关的 conf.all 节点要有 netfilter 模块。
解决:先sysctl -a | grep tcp_window_scaling确认当前有效的完整键名,照着抄进配置;改完用sysctl -w单独设置并在同一个终端cat /proc/sys/net/ipv4/tcp_window_scaling验证,确认后再写 sysctl.conf。如果键名没问题但不生效,检查是否用了 root 权限,普通用户写 /proc/sys 下的文件会静默失败。
5.2 tcp_tw_recycle 一开,NAT 后面的用户大面积连不上
现象:开启net.ipv4.tcp_tw_recycle=1后,部分用户反馈网页打不开,但服务器本机 curl 正常,日志里大量 connection reset。
原因:tcp_tw_recycle 启用了每连接的时间戳机制,并假设同一源 IP 的时间戳是单调递增的。NAT 后面多台设备共享一个公网 IP,不同设备的内核时间戳不同,后发数据包的时间戳可能比先发的小,内核直接丢弃,表现为间歇性不可达。
解决:只保留net.ipv4.tcp_tw_reuse=1,关掉 recycle。两者解决的问题有重叠,reuse 已经允许新连接复用 TIME_WAIT 端口,对绝大多数 Web 服务器足够。如果确实需要快速回收,改用net.ipv4.tcp_fin_timeout=30缩短等待时间,而不是强制回收。从那以后我配置 TIME_WAIT 参数时一律先确认网络出口是不是 NAT。
5.3 ulimit -n 改了 shell 里能看到,但服务进程仍然 1024
现象:登录后在终端执行ulimit -n 8192没问题,重启服务后发现进程打开的文件数还是 1024,用cat /proc/<pid>/limits查看确认。
原因:shell 的 ulimit 只影响当前会话和从它启动的子进程。服务如果是通过 systemd 启动的,systemd 有自己的 LimitNOFILE 设置,不读 /etc/security/limits.conf;如果是通过 /etc/init.d/sshd restart 启动的,要确认 pam_limits.so 被正确加载。
解决:systemd 管理的服务在 unit 文件里加LimitNOFILE=65535然后 daemon-reload;非 systemd 场景确认 /etc/pam.d/login 有session required pam_limits.so这一行。改完用cat /proc/<pid>/limits验证,不要只看 shell 的 ulimit -a。
5.4 云服务器上跑 hdparm,参数设置了等于没设
现象:在云服务器执行hdparm -d1 -X66 /dev/vda返回 OK,但再查hdparm -vI /dev/vda发现值没变,而且测试时磁盘性能没有提升。
原因:云服务器的磁盘是虚拟设备,DMA 模式和 IDE 传输模式由宿主机虚拟化层决定,guest 里的 hdparm 修改不了底层参数。hdparm 命令能执行成功只是驱动层面接受了写操作,实际没有硬件可调。
解决:云环境跳过 hdparm,直接换思路:用blktrace或iostat看真实 I/O 队列,确认是带宽瓶颈还是延迟瓶颈。本地物理机的 IDE 盘才值得调 hdparm,并且设置要写进 /etc/rc.local 防止重启丢失。
5.5 noatime 一加,依赖 atime 的应用开始抽风
现象:给 /var 分区加了 noatime 后,某个邮件系统开始把老邮件误判为新邮件,备份工具的行为也变了。
原因:atime 在部分应用里充当「是否已读」的判断依据。noatime 相当于永远不更新,应用只能看到一个旧时间戳,逻辑判断全乱。
解决:这类依赖 atime 的场景改用relatime。relatime 是折中方案:只有 atime 早于 mtime 或 ctime 时才更新,普通读操作不触发写盘,性能和 noatime 很接近,又兼容需要 atime 变化的应用。改 fstab 后重新挂载,mount -o remount,relatime /var即可。
6. 调优后的验证:用四条命令确认每一层配置真正生效
调优最怕的是改了一堆参数,实际没生效或者效果适得其反。我的习惯是每次调整后强制走一遍验证流程,四个命令各查一层。
第一层验证网络参数:sysctl -a | grep -E "syncookies|window_scaling|rmem|wmem|tw_reuse|fin_timeout",目的是确认当前内核实际生效的值。注意不要只看 /etc/sysctl.conf 里的内容,因为有些参数可能在别处(比如 /etc/sysctl.d/ 目录)被覆盖。
sysctl net.ipv4.tcp_syncookies net.ipv4.tcp_tw_reuse net.ipv4.tcp_fin_timeout第二层验证文件描述符限制:用ulimit -a确认当前会话的软限制,用cat /proc/<pid>/limits确认特定进程的实际限制。进程的 limits 文件是最终真相——如果 nginx 的 worker 进程显示 nofile 还是 1024,那 configuration 就还没生效。对 Java 这类频繁开 socket 的应用,这一条尤其重要。
第三层验证磁盘挂载参数:mount | grep noatime或者查 /proc/mounts,确认分区确实带上了你要的选项。fstab 里写了不代表挂载就用了,只有 /proc/mounts 里出现的 options 才是真实的。想快速测磁盘性能,用iostat -x 1看 await 和 %util,比hdparm -t更直接的判断瓶颈。
第四层验证内存回收行为:vmstat 1 10观察 si/so 是否为 0,cat /proc/sys/vm/dirty_ratio确认脏页阈值。如果调整了 overcommit_memory,建议在测试环境用stress工具模拟内存压力,确认系统不会意外 OOM。
最后记住一点:调优参数有强烈的场景相关性。文档里那份「激进组合」适合高带宽公网服务器,内网低并发场景盲目套用只会浪费内存。从那以后我每次调优前都先记录一份基线(sysctl -a 导出、free -m、iostat、vmstat),调完对照基线判断收益,而不是凭感觉说「好像快了」。这条习惯帮我挡掉了很多玄学调优,也希望帮到你。
本文还有配套的精品资源,点击获取