Apache HTTP Server 提供了一种基于共享内存的缓存机制,核心模块是 mod_cache_socache。它不会把缓存条目写进磁盘文件,而是通过一个 socache 后端把响应体保存在服务器可访问的内存区域。最常见的后端是 mod_socache_shmcb,它使用共享内存循环缓冲区来存储数据。理解它的容量模型,是调整缓存大小的前提。

一、shmcb 共享内存循环缓冲区的工作方式
shmcb 的容量由 CacheSocache 指令中括号里的数字决定,单位是字节。这个数字不是随意设置的,它直接对应共享内存段的大小。例如 524288 表示 512KB,268435456 表示 256MB。Apache 在启动时会创建或映射一块指定大小的共享内存区域,所有子进程都可以访问同一段内存,从而避免每个进程维护一份独立缓存。
循环缓冲区的特点是固定总容量,写入指针不断前进;当剩余空间不足以容纳新条目时,最旧的数据会被覆盖,或者写入失败。这跟磁盘缓存中“目录索引 + 文件存储”的模式完全不同。因此容量设置直接决定了缓存能容纳多少条目,以及淘汰发生的频率。容量过小会导致高频淘汰,命中率下降;容量过大又会消耗真实物理内存,影响整个系统的稳定性。
LoadModule cache_module modules/mod_cache.so LoadModule cache_socache_module modules/mod_cache_socache.so LoadModule socache_shmcb_module modules/mod_socache_shmcb.so CacheEnable socache / CacheSocache shmcb:/var/cache/apache/socache(524288)
上面示例中,524288 字节等于 512KB。注意这里不是 512k 这种写法,而是纯字节数。如果写成 512KB 或 512M,Apache 通常无法识别。共享内存路径 /var/cache/apache 必须对 Apache 运行用户可写,否则启动时会报权限错误。
二、如何确定合理的共享内存容量
先估算平均缓存条目大小。假设主要缓存 JSON 接口响应,平均 20KB;希望同时保留约 2000 个条目,那么数据部分约 40MB。shmcb 在数据之外还会维护一些索引和指针,实际内存占用通常会比纯数据大 10% 到 20%。所以可以设置约 48MB,约等于 50331648 字节。这个估算过程比单纯复制一个网上数值更可靠,因为不同业务的响应体差异很大。
不要一上来就给几个 GB。Apache 的 shmcb 分配的是真实物理内存,而且每个子进程都会映射同一段共享内存,不是每个进程复制一份,但调度和回收仍然受系统内存压力影响。如果服务器还运行数据库、PHP-FPM 或其他缓存服务,需要给它们留出足够空间。可以用 free -h 查看剩余内存,再用 ps -o rss,cmd -C httpd 观察进程内存占用。调整前先确认系统不会因为新增缓存而频繁使用 swap。
free -h
ps -o rss,cmd -C httpd | awk '{sum+=$1} END {print sum/1024 " MB"}'
ipcs -m
调整配置后,需要重新加载或重启 Apache。对于 shmcb 大小的修改,建议执行 systemctl restart httpd 或 apachectl restart,确保旧共享内存段被释放并重新创建。仅 reload 不一定能应用共享内存大小变化,因为共享内存段可能已经按旧容量分配。重启完成后可以用 ipcs -m 查看共享内存段是否与配置容量一致。
三、用 CacheSocacheMaxSize 控制条目体积
调整总容量只是第一步。如果单个响应体积过大,它会迅速占满循环缓冲区。mod_cache_socache 提供 CacheSocacheMaxSize 指令,用于限制可以写入共享内存的最大响应体字节数。默认值通常为 102400 字节,也就是 100KB。如果响应超过这个值,Apache 不会将其放入共享内存缓存,而是直接回源。这个阈值与共享内存总容量是两个独立维度,前者控制单个条目,后者控制整个缓存空间。
如果你的业务中经常返回 200KB 左右的接口数据,而且这些数据访问频率很高,可以考虑把 CacheSocacheMaxSize 调到 204800 或更高。但要注意,这会让单个条目占用更多空间,减少可缓存的条目总数。相反,如果主要流量是小对象,但偶尔有下载大文件经过同一缓存,建议保持默认值甚至调小,避免几个大文件把共享内存挤满。还可以结合 CacheDefaultExpire 和 CacheMaxExpire 控制条目存活时间,让空间更快回收。
<IfModule cache_socache_module>
CacheEnable socache /
CacheSocache shmcb:/var/cache/apache/socache(268435456)
CacheSocacheMaxSize 204800
CacheDefaultExpire 300
CacheMaxExpire 1800
</IfModule>
上面的配置把共享内存总容量设为 256MB,单个条目最大 200KB,默认过期 300 秒,最长 1800 秒。这样即使有一些稍大的响应,也能在可控范围内缓存。还需要注意,CacheSocacheMaxSize 修改后无需重建共享内存段,它只影响后续写入判断。但如果修改的是 CacheSocache 中的容量参数,则需要重启 Apache 才能完全生效。
四、验证调整结果和排查常见问题
配置生效后,可以用 curl 连续请求同一资源,观察响应头中的 Age 信息。如果命中共享内存缓存,Age 会从 0 开始增加,响应时间通常会明显下降。虽然 mod_cache 默认不输出 X-Cache,但可以通过响应时间和 Age 值来做判断。第一次请求通常来自源站,Age 可能不存在;第二次请求如果缓存命中,Age 会变为 1 或更大的数值。
curl -s -D - -o /dev/null http://127.0.0.1/test.json sleep 1 curl -s -D - -o /dev/null http://127.0.0.1/test.json
如果第二次响应头里出现 Age: 1 或更大的值,说明缓存已经命中。若配置了共享内存但始终没有命中,需要检查错误日志中是否出现 socache 相关的分配失败或写满提示。使用 tail -f /var/log/httpd/error_log 观察,当循环缓冲区容量不足时,通常会有类似 cache provider is full 或 could not allocate 的日志。这些信息是判断容量是否合理的直接依据。
另一个常见问题是:修改大小后没有重启,导致配置不生效;或者路径 /var/cache/apache 对 Apache 用户不可写,shmcb 无法创建共享内存标识。可以先用 apachectl configtest 检查语法,再执行 systemctl restart httpd。重启完成后用 ipcs -m 或 ls -l /var/cache/apache 查看共享内存文件是否出现。如果启动失败,优先检查可用内存和配置行末尾的括号是否成对。共享内存缓存调整最终要结合命中率、响应时间和系统内存曲线来持续优化,而不是一次改完就固定不动。