调整Linux内存交换和缓存行为的最佳实践

谨慎调整Linux的swappiness和VFS缓存行为,附有工作负载示例、sysctl命令和验证步骤。

调整Linux内存交换和缓存行为的最佳实践

Linux会使用空闲RAM。当用户第一次通过free -h看到服务器上“空闲”内存很少时,往往会感到惊讶。这些内存大部分可能是页面缓存、目录项和索引节点缓存,而不是内存泄漏。难点在于判断内核何时有效利用了RAM,以及内存回收何时开始影响应用程序。

在Linux内存调优中,有两个sysctl设置经常被提及:vm.swappinessvm.vfs_cache_pressure。它们很有用,但并非神奇的性能开关。错误的值可能掩盖真正的问题,或将压力从一个工作负载转移到另一个。

理解Linux内存管理参数

当系统需要更多空闲RAM时,Linux使用启发式算法来决定回收哪些内存页。由内核参数控制的两个主要领域是交换(将非活动内存页移至磁盘)和缓存(将文件系统元数据和数据保留在RAM中)。

1. vm.swappiness

vm.swappiness决定了内核将进程从物理内存移至磁盘交换空间的倾向。其值介于0到100之间。

  • 较高值(例如60,常见默认值): 内核更倾向于回收匿名内存,并将交换作为正常内存管理的一部分。这可以保留页面缓存,但如果活动页面被换出,也可能损害对延迟敏感的服务。
  • 较低值(例如10或更低): 内核更倾向于在交换进程之前从页面缓存中回收内存。这使正在运行的应用程序更长时间地保留在RAM中,提高了响应性,但如果系统需要不断丢弃缓存页面,可能会降低磁盘I/O性能。
  • 值为0: 内核尽可能避免交换,但这并不会禁用交换。如果需要禁用交换,请使用swapoff,并首先了解内存不足的风险。

vm.swappiness的实际应用

最佳设置高度依赖于工作负载:

工作负载类型 推荐的swappiness范围 理由
数据库服务器、高性能计算(HPC) 1 - 10 当交换延迟比丢弃缓存更糟糕时,通常是一个好的起点。请使用实际工作负载指标进行验证。
通用服务器、桌面 30 - 60 除非有证据表明交换行为正在损害系统,否则通常合理。
文件服务或缓存密集型系统 在某些情况下为60或更高 可以保留页面缓存,但仅当工作负载可以接受偶尔的交换时才合理。

如何检查当前值:

cat /proc/sys/vm/swappiness

如何临时更改值(重启前有效):

将swappiness设置为10:

sudo sysctl vm.swappiness=10

如何永久更改值:

编辑/etc/sysctl.conf文件,添加或修改行:

# /etc/sysctl.conf
vm.swappiness = 10

保存后,使用以下命令无需重启即可应用更改:

sudo sysctl -p

对于内存密集型数据库,110是常见的起点。不要将其视为规则。已经拥有自己缓冲缓存的数据库(如PostgreSQL或MySQL/InnoDB)通常受益于避免交换。文件服务器可能更喜欢较大的页面缓存。RAM过少的小型VM无论选择哪个数字都会受到影响。

2. vfs_cache_pressure

vfs_cache_pressure控制内核回收用于目录和索引节点元数据(VFS缓存)的内存的积极程度。

  • 该值范围从0到1000。
  • 默认值通常为100

值为100时,内核平衡回收VFS缓存内存与回收页面缓存(磁盘数据)使用的内存。值为100意味着当存在内存压力时,内核尝试每回收1部分页面缓存内存,就回收1部分索引节点/目录项缓存内存。

调整vfs_cache_pressure

  • 增加该值(例如> 100): 使内核更积极地回收VFS缓存内存。这可以更快地释放RAM,但可能导致后续文件系统查找变慢,因为需要从磁盘重新读取元数据。
  • 减少该值(例如< 100): 使内核在回收VFS缓存时更保守。这使目录和索引节点信息更长时间地保留在内存中,加快了重复的文件系统操作。

何时减少vfs_cache_pressure

如果系统频繁访问相同的大型目录结构(在复杂应用程序、容器编排或特定网络设置中常见),将此值设置得较低(例如50)可以通过将元数据保留在RAM中来提高性能。

何时增加vfs_cache_pressure

如果系统遭受普遍的内存压力,并且希望内核快速回收任何未使用的内存,可以增加此值,尽管这比降低它更不常见。

如何检查当前值:

cat /proc/sys/vm/vfs_cache_pressure

如何永久更改值:

编辑/etc/sysctl.conf

# /etc/sysctl.conf
vfs_cache_pressure = 50

使用sudo sysctl -p应用更改。

警告: 非常低的vfs_cache_pressure值可能导致内核比预期更长时间地保留目录和索引节点缓存。这可能有助于元数据密集型工作负载,但也可能使应用程序的内存压力恶化。除非测量了效果,否则避免极端值。

综合调优场景

选择这些参数的合适组合可以优化应用程序稳定性与文件系统缓存之间的权衡。

场景1:数据库服务器(内存优先)

目标:最大化应用程序内存驻留;不惜一切代价最小化交换。

  • vm.swappiness = 5
  • vfs_cache_pressure = 50(将目录数据缓存到一定程度,但如果RAM紧张,优先考虑应用程序内存而非VFS元数据)。

在更改任何内容之前,检查数据库是否实际正在交换:

free -h
vmstat 1
grep -E 'pswpin|pswpout' /proc/vmstat

如果在查询延迟峰值期间交换入和交换出计数器正在上升,降低swappiness可能会有所帮助。如果未使用交换且数据库速度慢,则swappiness不是问题。请检查查询计划、缓冲区命中率、检查点、磁盘延迟和连接压力。

场景2:高磁盘I/O服务器(缓存优先)

目标:通过将频繁访问的文件数据保留在页面缓存中来最大化磁盘性能。

  • vm.swappiness = 80(允许更早发生交换,以释放RAM用于磁盘缓存扩展)。
  • vfs_cache_pressure = 100(索引节点和页面缓存之间的标准平衡)。

这是人们最常过度调优的场景。如果服务器主要重复读取相同文件,页面缓存很重要。但如果系统开始交换活动工作进程以保留缓存,用户可能会看到更差的延迟,即使文件系统缓存看起来健康。请关注应用程序响应时间,而不仅仅是缓存大小。

场景3:虚拟化主机或通用系统

目标:跨多个工作负载的稳定性能。

  • vm.swappiness = 30(适中的设置,比默认的60更倾向于将活动VM/进程保留在RAM中稍长时间,但仍允许受控交换)。
  • vfs_cache_pressure = 100(默认通常足够)。

虚拟化主机需要格外小心,因为客户机内存行为可能会误导主机级调优。气球驱动、过度分配和客户机交换都可能相互作用。大量交换客户机内存的主机可能会在VM内部造成痛苦的延迟,即使每个客户机认为自己的工作负载正常。

更安全的调优工作流程

不要从编辑/etc/sysctl.conf开始。首先证明该设置是相关的。

  1. 在正常负载下捕获基线:

    free -h
    vmstat 1 10
    cat /proc/sys/vm/swappiness
    cat /proc/sys/vm/vfs_cache_pressure
    
  2. 在慢速期间捕获相同数据。添加进程级内存:

    ps -eo pid,comm,rss,vsz,%mem --sort=-rss | head -20
    
  3. 临时更改一个值:

    sudo sysctl vm.swappiness=10
    
  4. 运行工作负载足够长时间以观察行为。寻找更低的交换活动、更好的应用程序延迟以及没有新的文件系统减速。

  5. 只有在值通过现实测试窗口后才使其持久化。

在使用/etc/sysctl.d/的系统上,一个小的专用文件通常比附加到/etc/sysctl.conf更清晰:

sudo tee /etc/sysctl.d/90-memory-tuning.conf >/dev/null <<'EOF'
vm.swappiness = 10
vm.vfs_cache_pressure = 100
EOF

sudo sysctl --system

如果配置管理系统拥有sysctl设置,请将更改放在那里。手动编辑一个服务器上的sysctl很容易被遗忘且难以重现。

阅读free -h而不惊慌

典型的free -h输出可能显示free下的数字很小,而buff/cache下的数字很大。这是正常的。Linux将最近使用的文件数据保留在内存中,因为未使用的RAM对任何人都没有帮助。

关注available、交换使用情况以及当前是否正在发生交换活动。服务器可能因过去的内存峰值而分配了交换空间,但当前没有交换抖动。这比不断交换进出的服务器更不紧急。

使用:

vmstat 1

如果在正常负载下siso保持接近零,则交换此时并未主动驱动延迟。如果它们保持非零而应用程序停滞,则内存压力是严重嫌疑。

何时不调整这些设置

有几种情况,更改swappiness或缓存压力是错误的首次修复。

如果服务器没有配置交换,vm.swappiness几乎没有实际效果。您可能仍会为了策略一致性而调整它,但它本身无法解决内存压力。

如果交换仅作为小型紧急分区存在,该设置的作用也有限。内核可以选择何时使用交换,但它无法将几百兆字节的紧急空间变成真正的内存层级。在这种设置中,请关注OOM风险和服务限制。

如果进程存在真正的内存泄漏,降低swappiness会延迟痛苦。泄漏将继续增长。重启服务可能暂时恢复容量,但持久的修复是在应用程序层面:修补泄漏、限制内存、减少并发或更改工作负载。

如果磁盘因驱动器故障、存储限制或云卷饱和而变慢,内存调优可以减少一些读取,但无法修复存储故障。请检查iostat、内核日志、云卷指标和SMART/NVMe健康状态。

如果工作集大于RAM,则没有完美的sysctl值。您需要更多内存、更少的并发、更小的缓存、不同的数据布局或工作负载拆分。

容器和Kubernetes注意事项

在容器中,内存调优变得更加棘手。即使主机有空闲RAM,容器也可能达到其cgroup内存限制。主机的swappiness设置仍然重要,但直接症状可能是Pod或容器内的OOM杀死。

检查cgroup和编排器信号:

dmesg -T | grep -i 'killed process'
docker stats
kubectl describe pod <pod-name>

对于Kubernetes,更改节点级sysctl应作为节点池配置的一部分,而不是一次性的shell会话。还要记住,某些sysctl是命名空间化的,而某些是节点级的。vm.swappinessvm.vfs_cache_pressure在典型Linux系统上是主机级设置,因此更改它们会影响该节点上的每个工作负载。


监控和验证

应用更改后,持续监控对于验证影响至关重要。使用freevmstat和系统性能监控仪表板等工具。

使用vmstat

监控si(交换入)和so(交换出)列。具有低swappiness的健康系统在正常负载下应显示siso的低值或零值。

vmstat 5 10

procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----\ r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 0  0      0 123456 102400 5123456    0    0     0     5   40   70  1  1 98  0  0

如果在降低swappinessso值仍然很高,工作负载可能需要更多可用内存或更低的内存需求。更多RAM是一种答案,但不是唯一答案。您还可以减少工作进程数量、缩小应用程序缓存、调整数据库内存、修复泄漏或将服务拆分到多个主机。

vm.swappinessvm.vfs_cache_pressure视为工作负载偏好,而不是通用升级。实用的路径虽然枯燥但可靠:测量当前的交换和回收行为,更改一个设置,在实际负载下测试,并且仅在应用程序行为改善时保留更改。