第 23/30 天(重访)
引言
在《第 23 天:性能调优与内核参数》中,我们系统学习了 sysctl、ulimit、CPU 频率调节和 IO 调度器的基础操作。那篇文章解决了”怎么调“的问题,但生产环境真正缺的是”调什么、调到多少、怎么验证“。
今天这篇重访文章,我们将从运维工程师的视角重新审视性能调优:不是机械地套用网上的内核参数模板,而是先测量、再定位瓶颈、最后精准调优。我们会深入:
- 性能基准测试工具链——
sysbench、iperf3、fio、unixbench的正确用法 - 内存管理深层原理——page cache、NUMA、透明大页 THP 对数据库的影响
- TCP 网络栈生产级调优——BBR、连接跟踪、TIME_WAIT 的真实取舍
- cgroup 与 systemd 资源隔离——现代容器环境下的限制手段
- 生产环境调优踩坑实录——那些”看起来对却有害”的配置
核心思想:性能调优的本质是测量驱动的决策(measurement-driven tuning),而不是参数堆砌。没有基准数据支撑的调优,都是玄学。
一、先测量,再调优:建立性能基线
任何调优动作之前,必须先建立可量化的基线。下面是 Ubuntu 上最常用的性能基准工具。
1.1 安装工具链
# CPU / 内存 / 线程基准
sudo apt install -y sysbench
# 磁盘 IO 基准
sudo apt install -y fio
# 网络吞吐基准
sudo apt install -y iperf3
# 综合基准
sudo apt install -y unixbench
# 实时监控(确认调优前后差异)
sudo apt install -y htop iotop sysstat
1.2 CPU 与内存基准(sysbench)
# CPU 基准:计算素数,查看 events per second
sysbench cpu --threads=$(nproc) --cpu-max-prime=20000 run
# 内存基准:读写速度
sysbench memory --memory-total-size=4G run
# 线程基准:线程调度开销
sysbench threads --threads=64 run
示例输出:
CPU speed:
events per second: 1452.53
关键:调优后重新运行同样的命令,对比
events per second。提升少于 5% 的调整基本可以视为无效——不要留着徒增复杂度的配置。
1.3 磁盘 IO 基准(fio)
fio 参数繁多是初学者的噩梦,这里给出一个可靠的顺序读基准模板:
# 顺序读:测量最大吞吐
fio --name=seqread --rw=read --bs=1M --size=4G --numjobs=1
--runtime=30 --time_based --direct=1 --ioengine=libaio
# 随机读:测量 IOPS(数据库场景关键指标)
fio --name=randread --rw=randread --bs=4k --size=4G --numjobs=4
--runtime=30 --time_based --direct=1 --ioengine=libaio
--iodepth=32
输出解读: 重点关注 read: IOPS=..., BW=... 和 lat (usec)(延迟)。NVMe SSD 随机读 4K 通常应有数万 IOPS;HDD 则只有数百。
注意:
--direct=1绕过 page cache 直接读写磁盘,得到的是真实磁盘性能而非缓存性能。测试前确保磁盘有足够剩余空间(size=4G会占用 4G)。
1.4 网络基准(iperf3)
# 服务端(在目标机器上)
iperf3 -s
# 客户端(测试吞吐)
iperf3 -c <server_ip> -t 30
# 双向测试
iperf3 -c <server_ip> -t 30 --bidir
示例输出:
[ ID] Interval Transfer Bitrate
[ 5] 0.00-30.00 sec 6.28 GBytes 1.80 Gbits/sec
二、内存管理深层原理:page cache 与 THP
表面上看,vm.swappiness 调成 10 就是”尽量少用 swap”。但生产调优的坑远不止这些。
2.1 page cache 与脏页回写
Ubuntu 的读写会先经过 page cache,再由内核异步回写磁盘。dirty_ratio 和 dirty_background_ratio 控制回写时机:
# 查看当前脏页参数
sysctl vm.dirty_ratio vm.dirty_background_ratio vm.dirty_writeback_centisecs
# 数据库服务器常用配置(避免突发大回写导致 IO 尖峰)
cat > /etc/sysctl.d/99-db.conf << 'EOF'
# 后台回写阈值:内存的 5% 即开始后台回写
vm.dirty_background_ratio = 5
# 同步回写阈值:内存的 30% 时进程被迫同步等待
vm.dirty_ratio = 30
# 回写周期:5 秒(默认 500 厘秒)
vm.dirty_writeback_centisecs = 500
EOF
sudo sysctl --system
原理:
dirty_background_ratio到了才在后台异步回写(不阻塞应用);dirty_ratio到了则同步阻塞应用等待回写。对数据库(依赖 fsync 保证持久性),dirty_ratio过高会导致崩溃时丢失数据窗口变大,一般建议 20-30。
2.2 透明大页 THP:数据库的隐形杀手
THP(Transparent Huge Pages)默认开启,对内存分配粒度大的应用(数据库、JVM)反而有害——会导致内存碎片和延迟抖动。MySQL/MongoDB 官方文档都明确建议关闭:
# 查看当前状态
cat /sys/kernel/mm/transparent_hugepage/enabled
# 关闭(运行时)
echo never | sudo tee /sys/kernel/mm/transparent_hugepage/enabled
# 永久关闭(内核引导参数)
sudo sed -i 's/GRUB_CMDLINE_LINUX_DEFAULT="(.*)"/GRUB_CMDLINE_LINUX_DEFAULT="1 transparent_hugepage=never"/'
/etc/default/grub
sudo update-grub
实践:运行 MySQL 的 Ubuntu 服务器,应关闭 THP 并同时设置
vm.swappiness=1(MySQL 官方建议),否则 swap 换页会拖垮 InnoDB 缓冲池。
2.3 NUMA 感知:多路服务器的关键
多路(multi-socket)服务器的内存访问存在 NUMA 距离差异。用 numactl 检查并绑定:
# 安装并查看 NUMA 拓扑
sudo apt install -y numactl
numactl --hardware
# 查看进程当前的 NUMA 分配
numastat -p <pid>
# 让 MySQL 绑定到 Node 0 的 CPU 和内存
numactl --cpunodebind=0 --membind=0 mysqld_safe &
三、TCP 网络栈生产级调优
网上传播的”网络优化参数”鱼龙混杂,很多早已过时甚至有害。下面给出经得起生产检验的配置。
3.1 现代内核推荐配置(Ubuntu 22.04+)
cat > /etc/sysctl.d/99-network.conf << 'EOF'
# ===== 拥塞控制:BBR(需内核 4.9+,Ubuntu 20.04 起默认支持)=====
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr
# ===== 缓冲区:为高带宽延迟积(BDP)预留空间 =====
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.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
# ===== 连接跟踪(高并发时必须调大)=====
net.netfilter.nf_conntrack_max = 1048576
net.netfilter.nf_conntrack_buckets = 262144
# ===== TIME_WAIT 处理 =====
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 30
EOF
sudo sysctl --system
3.2 关于 tcp_tw_reuse 的重要提醒
tcp_tw_reuse 只对出站连接生效(客户端角色),对作为服务器的监听套接字上大量 TIME_WAIT 的入站连接无效。服务器端 TIME_WAIT 过多时,正确做法是:
# 查看当前 TIME_WAIT 数量
ss -tan state time-wait | wc -l
# 方法一:调短 fin_timeout(谨慎,可能影响对端收到 FIN 的可靠性)
sudo sysctl -w net.ipv4.tcp_fin_timeout=20
# 方法二(更推荐):让应用复用连接(连接池 / keep-alive),从源头减少 TIME_WAIT
# 例如 Nginx 配置 upstream keepalive
误区纠正:不要盲目开启已废弃的
tcp_tw_recycle(内核 4.12 已移除)。它曾在 NAT 环境下引发严重的连接问题。
3.3 检查连接跟踪是否打满
高并发防火墙/网关场景,连接跟踪表是常见瓶颈:
# 当前使用量 / 上限
cat /proc/sys/net/netfilter/nf_conntrack_count
cat /proc/sys/net/netfilter/nf_conntrack_max
# 达到上限时 dmesg 会出现:
# "nf_conntrack: table full, dropping packet"
四、cgroup 与 systemd 资源隔离:UBUNTU 现代调优手段
传统 ulimit 只限制单个进程的资源。在容器化盛行的今天,cgroup v2 + systemd 才是更精细、生产必备的资源控制手段。
4.1 systemd 服务的 CPU/内存限制
# 为服务创建 override 目录
sudo mkdir -p /etc/systemd/system/mysql.service.d
sudo tee /etc/systemd/system/mysql.service.d/resources.conf << 'EOF'
[Service]
# 限制内存上限(含 page cache)
MemoryMax=8G
MemoryHigh=6G
# 限制 CPU 使用率(100% = 1 核)
CPUQuota=200%
# 限制文件句柄
LimitNOFILE=65535
EOF
sudo systemctl daemon-reload
sudo systemctl restart mysql
4.2 查看 cgroup 实际用量
# 查看某服务的 cgroup 资源使用
systemctl status mysql | grep -E "Memory|CPU"
# 或直接读 cgroup v2 文件
cat /sys/fs/cgroup/system.slice/mysql.service/memory.current
cat /sys/fs/cgroup/system.slice/mysql.service/cpu.max
cgroup v2 优势:
MemoryMax是硬限制(超了 OOM),MemoryHigh是软压力阈值(超了内核开始回收,但不立即杀进程)。这比传统ulimit -v精确得多。
五、生产环境踩坑实录
以下是几个真实生产环境里”看着对、实则有害”的经典案例,务必引以为戒。
坑 1:盲目套用”万能调优脚本”
现象:服务器装了网上流传的”一键优化脚本”,把所有内核参数都拉满,结果数据库延迟反而飙升。
原因:vm.swappiness=0(彻底禁用 swap)在没有足够内存的机器上会导致 OOM 直接杀进程;tcp_tw_recycle=1 在 NAT 后端引发随机连接失败。
对策:一次只改一个参数,改完用基准工具验证,保留变更记录,回退方便。
坑 2:只调内核参数,不调应用层
现象:fs.file-max 调到 2097152,Nginx 仍报 “too many open files”。
原因:三层的限制都要满足——系统级(fs.file-max)、用户级(limits.conf)、进程级(systemd LimitNOFILE)。三层中最低者生效。
# 完整检查三层限制
cat /proc/sys/fs/file-max # 系统级
ulimit -n # 用户级(当前 shell)
cat /proc/$(pidof nginx)/limits | grep "files" # 进程级(最准确)
坑 3:基准测试方法错误导致误判
现象:用 dd 测磁盘,得到惊人速度,但生产一跑就慢。
原因:dd 默认走 page cache,测到的是内存速度而非磁盘速度。
对策:用 fio --direct=1(绕过缓存)或 dd oflag=direct:
# 正确的 dd 磁盘测速(绕过 page cache)
dd if=/dev/zero of=/tmp/test bs=1M count=1024 oflag=direct conv=fdatasync
六、总结与下期预告
性能调优不是”参数越多越好”,而是一套测量 → 定位 → 调整 → 验证的科学流程。今天你学到的核心要点:
- 先建立基线:用
sysbench/fio/iperf3量化当前性能,调优才有对照 - 内存调优看场景:数据库关 THP、调 dirty_ratio;NUMA 多路服务器用
numactl绑定 - 网络调优重实证:BBR 值得开、
tcp_tw_reuse只对出站有效、tcp_tw_recycle已废弃 - 现代资源控制用 cgroup v2:
MemoryMax/CPUQuota远比传统 ulimit 精细 - 一次只改一个参数:勤验证、留记录、可回退
遇到性能问题,先问”数据在哪”,再动手。没有测量,就没有调优。
下一篇(第 24 天)我们将进入虚拟化技术:在 Ubuntu 上使用 KVM/QEMU、libvirt 和 Virt-Manager 搭建虚拟化环境,敬请期待!
系列目录
| 天数 | 主题 | 状态 |
|---|---|---|
| 第 1 天 | Ubuntu 简介与版本选择 | ✅ |
| 第 2 天 | 手把手安装 Ubuntu | ✅ |
| 第 3 天 | Ubuntu 桌面环境初探 | ✅ |
| 第 4 天 | Ubuntu 终端基础 | ✅ |
| 第 5 天 | 用户与权限管理 | ✅ |
| 第 6 天 | 软件包管理 | ✅ |
| 第 7 天 | 文件与文本操作 | ✅ |
| 第 8 天 | 系统服务管理 | ✅ |
| 第 9 天 | 磁盘与文件系统管理 | ✅ |
| 第 10 天 | 网络配置与管理 | ✅ |
| 第 11 天 | 进程管理与监控 | ✅ |
| 第 12 天 | 计划任务与自动化 | ✅ |
| 第 13 天 | 备份与恢复策略 | ✅ |
| 第 14 天 | 系统更新与升级管理 | ✅ |
| 第 15 天 | SSH 远程管理与安全加固 | ✅ |
| 第 16 天 | Web 服务器搭建 | ✅ |
| 第 17 天 | 数据库服务器部署 | ✅ |
| 第 18 天 | Docker 容器化管理 | ✅ |
| 第 19 天 | 文件共享服务 | ✅ |
| 第 20 天 | 监控与告警系统 | ✅ |
| 第 21 天 | 邮件服务器基础 | ✅ |
| 第 22 天 | 系统安全加固 | ✅ |
| 第 23 天 | 性能调优与内核参数(重访) | 🟢 今日 |
| 第 24 天 | 虚拟化技术 | ⏳ |
| 第 25 天 | 容器编排入门 | ⏳ |
| 第 26 天 | 高可用与负载均衡 | ⏳ |
| 第 27 天 | Ubuntu 自动部署 | ⏳ |
| 第 28 天 | 故障排查实战 | ⏳ |
| 第 29 天 | Ubuntu 社区与文档 | ⏳ |
| 第 30 天 | 30 天回顾总结 | ⏳ |















暂无评论内容