Linux 性能优化实战:打通磁盘、内存、TLB 与 Cache

> 来源:基于线上服务调优经验与 Linux 常用性能工具链整理。

一、为什么很多优化会失败

线上性能问题经常不是单点瓶颈,而是链路瓶颈。

一个典型误区是:

结果往往“局部变快,全局变慢”。根因是没有把链路打通看。

二、先建立链路视角:磁盘 -> 页缓存 -> 内存回收 -> TLB -> CPU Cache

1. 读请求链路


应用 read()/mmap() 读文件
  -> 先查页缓存(Page Cache)
     -> 命中:直接返回(内存带宽和 CPU cache 行为主导)
     -> 未命中:触发块设备 IO(磁盘/NVMe)
  -> 页面进入内存后,需要页表映射
  -> CPU 通过 TLB 缓存虚拟地址转换
  -> 最终访问 L1/L2/L3 cache 与 DRAM

2. 写请求链路


应用 write()
  -> 先写页缓存(脏页)
  -> 内核后台回写(writeback)到磁盘
  -> 脏页比例过高时,前台线程可能被迫回写(卡顿)

3. 链路上的关键耦合

所以优化要按链路做,不要按单点做。

三、内存优化深挖:这是性能稳定性的核心

1. 先分清“内存在干什么”

Linux 内存大致分三块视角:

很多“内存占用高”其实是页缓存高,这本身不一定是坏事。 坏信号是:回收抖动、swap 抖动、major fault 持续升高。

2. 必看的内存指标与含义

全局指标


vmstat 1
cat /proc/meminfo
sar -B 1
sar -W 1

重点观察:

进程级指标


pidstat -r -p <pid> 1
cat /proc/<pid>/status
cat /proc/<pid>/smaps_rollup

重点看:

3. 回收路径:kswapd 与 direct reclaim

两类回收行为会带来非常不同的体感:

如果线上出现“偶发几百毫秒到秒级卡顿”,要优先排查 direct reclaim。

可观察:


cat /proc/vmstat | egrep 'pgscan|pgsteal|allocstall|kswapd'

allocstall 上升通常意味着应用线程被迫进入 direct reclaim。

4. swap 策略不是越低越好

常见经验是把 vm.swappiness 拉低,但这要看业务形态:

建议:先压测再定,不要用单机经验直接套所有节点。

5. 脏页与回写参数

写多场景里,脏页策略会直接影响尾延迟。

关键参数:

理解方式:

如果你看到“平时很快,偶发写入卡顿”,通常是脏页回写波峰过大。

6. THP / HugePage 与 TLB 的关系

TLB 缓存“虚拟地址 -> 物理地址”映射。

所以 THP 有机会提升性能,但不是无脑开启就一定好:

建议用 A/B 压测验证 always/madvise/never,同时看 P99 而不只看平均值。

7. NUMA:远程内存访问会“偷走”吞吐

多路服务器常见问题:线程跑在 NUMA0,内存分配在 NUMA1。

后果:

工具:


numactl --hardware
numastat -p <pid>

优化方向:

8. 容器场景:cgroup 内存限制与 OOM

在 Kubernetes 或容器环境里,很多“系统还有空闲内存但服务被杀”的问题,本质是 cgroup 内存超限。

重点排查:

常见策略:

9. 内存碎片与 compaction 抖动

如果需要大块连续页(THP 或驱动分配),内存碎片会触发 compaction,造成短时延迟尖峰。

可观测信号:

实战建议:

四、TLB 与 CPU Cache:很多“CPU 高”本质是访存低效

1. TLB miss 为什么会拖慢系统

每次虚拟地址访问都需要地址翻译:

在随机访问、工作集远大于 TLB 覆盖范围时,性能会明显下滑。

2. CPU Cache miss 的代价

访问层级延迟大致是:L1 < L2 < L3 << DRAM。

如果数据局部性不好:

常见诱因:

3. 可用观测手段


perf stat -e cycles,instructions,cache-references,cache-misses,dTLB-load-misses,iTLB-load-misses -p <pid> -- sleep 30
perf top -p <pid>

可以重点看:

这些指标能帮你判断是“算力不足”还是“访存效率低”。

五、磁盘优化要和内存联动看

1. 不要只盯 %util

iostat -x 里更有价值的是:

%util 接近 100% 在 NVMe 上不总是“已到极限”,要结合延迟与队列一起看。

2. 页缓存命中率决定了磁盘压力上限

如果业务随机读多,内存给页缓存不够,命中率会下降,磁盘 await 会抬高。

优化不是“一味加磁盘”,而是:

3. 写路径的典型抖动

写入突发时:

你会看到业务延迟突然拉长,同时磁盘带宽并不一定跑满。

六、CPU 优化:把“争用”和“抖动”优先消掉

核心思路:

常用命令:


mpstat -P ALL 1
pidstat -u -t -w -p <pid> 1
perf top -p <pid>

高频收益项:

七、网络与内存也有强耦合

网络吞吐上去后,经常是内存子系统先告警:

观测:


ss -s
sar -n TCP,DEV 1
ethtool -S <nic>
cat /proc/softirqs

八、一个可执行的排障流程(建议固化到值班手册)

1. 先确认症状:吞吐下降、P99 升高、错误率变化。 2. 快速分层:CPU、内存、磁盘、网络谁先异常。 3. 拉链路指标: - 磁盘:iostat -x 1 - 内存:vmstat 1 + /proc/vmstat - TLB/Cache:perf stat 4. 建立因果:例如“页缓存下降 -> 磁盘 await 升高 -> 请求延迟升高”。 5. 小步调参:每次只改一类参数,保留回滚。 6. 压测复验:看 P95/P99、稳定性和波动幅度。

链路判定示例:

九、参数建议(起点,不是标准答案)


# 低延迟服务常见起点
vm.swappiness = 10
vm.dirty_background_ratio = 5
vm.dirty_ratio = 20

# 高并发网络服务常见起点
net.core.somaxconn = 4096
net.ipv4.tcp_max_syn_backlog = 8192

# 文件句柄上限
fs.file-max = 1048576

这些参数必须结合你的业务负载验证,尤其是写密集和内存敏感场景。

十、优化手段全集(按层落地)

1. 数据结构与代码层

2. 结构体 cache line 对齐:什么时候会更快

你的判断方向是对的,但要加边界条件。

结论:

示例(C/C++):


#include <atomic>

// 常见 x86 cache line 为 64B(不同平台可能不同)
struct alignas(64) ShardCounter {
  std::atomic<uint64_t> value;
  char pad[64 - sizeof(std::atomic<uint64_t>)];
};

适用场景:

不建议场景:

3. 内存访问局部性优化

4. 内存分配器与运行时策略

5. 页缓存与回写链路

6. TLB/THP/NUMA 联动优化

7. CPU 与调度层

8. 磁盘与文件系统层

9. 网络栈层

10. 工程方法层

十一、总结

Linux 性能优化最怕“单点调优”,最有效的是“链路调优”。

一条实用原则:

把这三层打通,绝大多数线上性能问题都能更快定位、更稳优化。

评论