Ubuntu 系统系列 | 第 23 天(重访):性能调优与内核参数——生产环境基准测试与踩坑实战

第 23/30 天(重访)

引言

在《第 23 天:性能调优与内核参数》中,我们系统学习了 sysctlulimit、CPU 频率调节和 IO 调度器的基础操作。那篇文章解决了”怎么调“的问题,但生产环境真正缺的是”调什么、调到多少、怎么验证“。

今天这篇重访文章,我们将从运维工程师的视角重新审视性能调优:不是机械地套用网上的内核参数模板,而是先测量、再定位瓶颈、最后精准调优。我们会深入:

  • 性能基准测试工具链——sysbenchiperf3fiounixbench 的正确用法
  • 内存管理深层原理——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_ratiodirty_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 v2MemoryMax / 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 天回顾总结
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片快捷回复

    暂无评论内容