腾讯云服务器性能优化:内核参数、TCP 队列与文件句柄的上限
正文
很多人一遇到卡顿,第一件事就是搜索"腾讯云服务器性能优化",然后照着帖子把一堆内核参数改一遍,结果瓶颈没解决,系统反而不稳定。我在这行见得太多这种误诊:CPU 明明空闲,真正的问题出在文件句柄打满或者磁盘 IOPS 撞墙,改 TCP 参数自然没用。调优的第一步从来不是改参数,而是先量出瓶颈在哪。
先定位瓶颈,再动参数:四个维度的排查顺序
我会让客户按这个顺序看:先看负载类型,用 uptime 看 load,判断是 CPU 型还是 IO 等待型;再看内存与换页,用 free -m 确认有没有在用 swap;然后看磁盘,用 iostat -x 1 看 %util 与 await;最后才看网络,看连接数、TIME_WAIT 和重传。这四层的表现会互相伪装:swap 抖动看起来像 CPU 卡,磁盘 await 飙高会让应用线程全堵住,网络半连接队列溢出则表现为"连接时快时慢"。
定位清楚再动手,改一个参数、测一轮、留一条记录,比一次性改十个参数靠谱得多。看不准瓶颈就调参,等于蒙着眼睛换零件。
再补一个判断优先级的小经验:如果 load 高但 CPU 使用率不高,十有八九卡在 IO 或锁等待;如果 CPU 使用率高、load 也高,才是真正的计算瓶颈;如果内存足、盘不忙、CPU 也闲,却时不时卡一下,那多半是网络队列或外部依赖(数据库、第三方接口)在拖。把这几种指纹先记住,排查能少走一大半弯路。定位阶段用 vmstat 1 看 r(运行队列)、b(阻塞进程)、si/so(换页)三个字段,往往一眼就能判断方向。
文件句柄与进程上限:ulimit、systemd 与内核 fs 限制
一个被低估的高频瓶颈是文件句柄。Linux 单进程 ulimit -n 常见默认是 1024,Nginx、MySQL、Java 这类高并发进程在几千连接时就打满,报错形态往往是 Too many open files。要特别注意三处限制是叠加的:内核全局 fs.file-max、用户级的 /etc/security/limits.conf,以及用 systemd 托管服务时的 LimitNOFILE。
systemd 启动的服务在很多发行版上不读 limits.conf 的那几行,只认 unit 文件里的 LimitNOFILE,这个坑我踩过不止一次,明明改了配置、重启后 ulimit -n 还是 1024。排查方法很直接:用 cat /proc/<pid>/limits 看进程真实上限,用 lsof -p <pid> | wc -l 看实际占用,别只盯着配置文件。改完这类限制通常要重启进程才生效,重启窗口得提前约。
调多大合适?我一般按"预计并发连接数乘以每连接句柄数,再留一倍余量"估。Nginx 每个连接约占 2 个句柄(客户端加后端各一),加上日志文件、上游连接池,几千并发轻松过万,所以生产机上 nofile 开到几万并不夸张。除了文件句柄,inotify 监听数(fs.inotify.max_user_watches)是另一个隐形上限,开发机和文件同步类服务经常被它卡住,报错同样是资源耗尽却容易误判。
TCP 队列与 TIME_WAIT:backlog、somaxconn 与端口复用
短连接多的服务,瓶颈常在 TCP 队列。两个队列要分清:半连接队列受 net.ipv4.tcp_max_syn_backlog 影响,全连接队列由应用 listen 的 backlog 与 net.core.somaxconn 共同决定,队列满了内核会丢握手,表现为偶发连接超时。可以看 netstat -s 里的 SYN 丢弃计数,用 ss -lnt 观察 Send-Q(全连接队列上限)与 Recv-Q(当前排队数)。
TIME_WAIT 本身不是故障,而是 TCP 的正常状态,短连接服务里出现几万个 TIME_WAIT 并不稀奇。想缓解端口耗尽,可以用 net.ipv4.tcp_tw_reuse 复用出向连接、扩大 net.ipv4.ip_local_port_range、适当调小 net.ipv4.tcp_fin_timeout。但要清楚 tcp_tw_recycle 在新内核已被移除,别再抄旧帖。真正的解法往往是加长连接、上连接池,而不是硬压 TIME_WAIT 的数量。
排查队列问题还要看两个全局信号:用 ss -s 看 sockets 总览里 TIME-WAIT、SYN-RECV 的数量是否异常增长;用 netstat -s 关注"listen queue overflow"(全连接队列溢出)和"SYN to LISTEN dropped"(半连接被丢)。如果只有应用侧的 backlog 调大、而 net.core.somaxconn 没跟着调,溢出照样发生,两头必须一起改。另外 net.ipv4.ip_local_port_range 默认范围在短连接密集场景下很容易耗尽,表现为客户端报"cannot assign requested address",这一点在压测出向接口时会立刻暴露。
连接数上限、swap 与透明大页的取舍
连接数上限还受 net.core.somaxconn、net.ipv4.ip_local_port_range 和 conntrack 表大小(net.netfilter.nf_conntrack_max)共同约束。做 NAT 或高并发转发的场景,conntrack 表打满会出现"随机丢包",很难查。
swap 和透明大页是两类容易帮倒忙的"优化"。vm.swappiness 调高会让内核更积极换出内存,数据库类进程反而抖动,很多场景把它设低是合理的。透明大页(THP)在数据库上常引发延迟尖刺,把 /sys/kernel/mm/transparent_hugepage/enabled 设为 never 或 madvise 是常见做法,但这属于按业务判断,不是无脑关。
关于 swap 我的经验是:不是"有 swap 就关",而是"别让热数据落到 swap"。用 vmstat 1 看 si、so 两列,长时间持续不为零,才说明内存确实不够,这时加内存或优化程序占用比调 swappiness 更治本。反过来,如果只是偶尔换出一小部分冷页,完全不必惊慌。内存类的调优最容易变成心理安慰——参数改了一堆,内存缺口还在,业务一上量照样 OOM。真要看内存够不够,盯 available 而不是 free 更准,Linux 会把缓存也算进去,free 显示"几乎没剩"往往并不代表危险。
调优维度 关键参数或指标 常见默认 调整方向 主要风险
文件句柄 ulimit -n 与 systemd LimitNOFILE 单进程常见 1024 按并发连接数上调并留余量 只看一处配置会漏 systemd 限制
半连接队列 net.ipv4.tcp_max_syn_backlog 视内核版本而定 与 somaxconn 一起调大 内存不足时反噬稳定性
全连接队列 net.core.somaxconn 与 listen backlog 常见 128 到 1024 与业务并发匹配上调 应用侧 backlog 不调仍会丢连接
端口与 TIME_WAIT tcp_tw_reuse 与 ip_local_port_range 端口范围偏小 复用出向连接并扩大端口范围 旧参数 tcp_tw_recycle 已移除
内存与换页 vm.swappiness 与 THP swappiness 常见 60 按业务评估后下调 盲目关闭可能影响其他负载
磁盘性能 iostat 的 util 与 await 受云硬盘类型限制 升级增强型 SSD 或加缓存 参数无法突破硬件 IOPS 上限
磁盘 IO 与云硬盘类型:别用参数掩盖硬件瓶颈
参数治不了硬件上限。云服务器 CVM(行业里常叫 ECS)的磁盘性能取决于云硬盘类型:普通云硬盘、SSD 云硬盘、增强型 SSD 的 IOPS 与吞吐逐级提升,小规格实例还有"基准加突发"的 IOPS 上限,买之前就要按业务的随机读写量核对,别等到线上告警才发现选低了。如果 iostat 显示 %util 长期接近 100%、await 高企,而 CPU 和内存都松,那就是盘到顶了。
这种情况下,换云硬盘类型、做读写分离、加本地缓存,比继续调内核立竿见影。判断顺序建议是:先用 iostat -x 和 iotop 确认是随机读写还是顺序读写,再决定是升级 CBS 云硬盘类型还是改架构。调参能优化调度,但不能变出物理 IOPS,认清这条线能省掉大量无效工作。
顺序写和随机写的差别也要分清:备份、日志追加这类顺序写瓶颈多在吞吐,加大块和缓冲就有改善;数据库随机读写瓶颈在 IOPS,缓冲帮助有限,只能靠更高档的云硬盘。机械盘的调度器参数(如 noop、mq-deadline)在云上多数已被虚拟化层屏蔽,改了基本没感觉,这一点别浪费时间去试。
还要提醒一个常见陷阱:把应用层的慢误判成磁盘慢。连接池配置过小、数据库缺少合适索引、代码里同步写日志,都会让 await 看起来很高,但根因在应用。我的排查习惯是先看 iotop 到底是哪个进程在写,再看它写的是什么模式,最后才怀疑磁盘硬件。云硬盘的吞吐和 IOPS 有明确上限,规格页写得清清楚楚,先核对规格再动手,比盲目调参省事得多。
实例规格与 CPU 积分:参数之外的性能上限
云服务器 CVM 的规格族(如 SA3、S5、M6 这类命名)决定了 vCPU 与内存配比、基线性能和网络收发包能力,实例规格才是一切调优的物理天花板。一个特别容易被误诊的坑是突发性能实例:它靠 CPU 积分换取短时高负载,积分耗尽后会被限速到很低的基线,表现就是"压测一开始飞快、跑久了性能断崖式下跌",很多人以为是自己参数没调好。
判断方法:用 top 看 %st(steal 时间)判断有没有被宿主机争抢,用 vmstat 1 的 r 列看运行队列是否长期大于 vCPU 数,再结合实例规格文档核对基线性能。持续吃 CPU 的负载,比如数据库、编译、转码,就不该选突发型或积分受限的规格。参数调不出来的是规格上限,买错规格,再努力调优也只是徒劳。
调参的风险、回滚与变更纪律
调参是有风险的:vm.overcommit_memory 配错、把 somaxconn 设得远超内存承受能力、误用已被移除的参数,都可能带来稳定性问题。我的纪律是:先在测试机验证,改动写进 /etc/sysctl.d/ 下的独立文件而不是直接改 sysctl.conf,一条一注释,用 sysctl -p 生效并记录版本;出问题能单文件回滚。
生产高峰前不做调参变更,ulimit 这类改完必须重启进程才生效,重启窗口要提前和业务方约。所有改动都留一份基线快照,改前改后各测一轮同样的压测脚本,用数据说话而不是凭感觉。
压测本身也有讲究:要区分"连接数打满"和"吞吐打满",用固定并发逐步加压,记录每秒新建连接、P99 响应时间、错误率三个指标,而不是只看平均 QPS。平均值会掩盖长尾抖动,真实的性能问题往往藏在 P99 和超时率里。有条件的话在测试环境复现生产流量比例,读写比例、连接复用率对结论影响很大,用纯读压测得出的结论套到写密集业务上,很容易翻车。每次调整只改一个变量,观察指标是否稳定复现,再进入下一步,这样才能分清是调参起了作用,还是当天流量本来就低。
FAQ
Q:腾讯云服务器性能优化,第一步到底该做什么?
A:先定位瓶颈。按 CPU、内存与 swap、磁盘、网络四层依次看,用 uptime、free -m、iostat -x 1 拿到数据,判断是计算、内存、IO 还是队列问题,再决定改哪个参数或升哪类资源,别上来就改内核,也不要一次改多个变量。
Q:ulimit 改了为什么进程上限还是 1024?
A:多半是服
3 .0
