06 - 缓存管理
第六章:缓存管理
6.1 缓存机制概述
Squid 的缓存系统是其核心能力之一,通过将频繁访问的内容存储在本地,减少对源站的请求次数和网络带宽消耗。
客户端请求
│
▼
┌───────────────────────┐
│ Squid 缓存查询 │
│ │
│ ┌──────────┐ │
│ │ 内存缓存 │ ← 命中 │ → 直接返回(微秒级)
│ │ cache_mem │ │
│ └────┬─────┘ │
│ │ 未命中 │
│ ▼ │
│ ┌──────────┐ │
│ │ 磁盘缓存 │ ← 命中 │ → 验证新鲜度 → 返回(毫秒级)
│ │ cache_dir │ │
│ └────┬─────┘ │
│ │ 未命中 │
└───────┼───────────────┘
▼
转发到源站/父代理(秒级)
│
▼
缓存响应并返回客户端
6.2 缓存规则配置
6.2.1 基础缓存目录
# UFS 存储(适合小型环境)
cache_dir ufs /var/spool/squid 5000 16 256
# AUFS 存储(推荐中型环境)
cache_dir aufs /var/spool/squid 20000 256 4096
# Rock 存储(适合高并发小对象)
cache_dir rock /var/spool/squid/rock 10240 max-size=32768
# 内存缓存
cache_mem 1024 MB
maximum_object_size_in_memory 512 KB
# 磁盘对象大小限制
maximum_object_size 256 MB
minimum_object_size 0 KB
6.2.2 refresh_pattern 规则
refresh_pattern 控制 Squid 如何判断缓存对象的新鲜度:
# 语法:
# refresh_pattern [-i] regex min percent max [options]
#
# min: 最小过期时间(分钟)
# percent: 最后修改时间的百分比
# max: 最大过期时间(分钟)
# options:
# override-expire 覆盖源站的 Expires 头
# override-lastmod 覆盖 Last-Modified 头
# reload-into-ims 将 reload 请求转为 If-Modified-Since
# ignore-reload 忽略客户端的 Cache-Control: no-cache
# ignore-no-cache 忽略源站的 no-cache 指令
# ignore-private 忽略 private 指令
# ignore-auth 忽略需认证的响应
6.2.3 分类型缓存策略
# 静态图片 — 长期缓存(7天)
refresh_pattern -i \.(jpg|jpeg|png|gif|ico|webp|avif|svg)$ \
10080 90% 43200 override-expire override-lastmod
# CSS/JS — 中期缓存(1天)
refresh_pattern -i \.(css|js|mjs|woff2|woff|ttf|eot)$ \
1440 90% 43200 override-expire override-lastmod
# 视频/音频 — 长期缓存(30天)
refresh_pattern -i \.(mp4|webm|ogg|mp3|wav|flac)$ \
43200 90% 86400 override-expire override-lastmod
# 文档 — 中期缓存(1天)
refresh_pattern -i \.(pdf|doc|docx|xls|xlsx|ppt|pptx|zip|tar|gz)$ \
1440 80% 43200 override-expire override-lastmod
# API 请求 — 不缓存
refresh_pattern ^/api/ 0 0% 0
refresh_pattern ^/ajax/ 0 0% 0
refresh_pattern ^/graphql 0 0% 0
# 首页 — 短期缓存(5分钟)
refresh_pattern ^/$ 5 100% 10
refresh_pattern ^/index\.html$ 5 100% 10
# 兜底规则(默认5分钟-1天)
refresh_pattern . 60 20% 1440
6.2.4 refresh_pattern 匹配顺序
请求 URL
│
▼
refresh_pattern 规则 1 (.jpg) → 匹配? → 应用此规则
│ 未匹配
▼
refresh_pattern 规则 2 (.css) → 匹配? → 应用此规则
│ 未匹配
▼
refresh_pattern 规则 3 (/api/) → 匹配? → 应用此规则
│ 未匹配
▼
refresh_pattern 规则 N (兜底) → 必然匹配
注意:
refresh_pattern规则按配置文件中的顺序匹配,第一条匹配的规则生效。兜底规则必须放在最后。
6.3 缓存验证机制
6.3.1 新鲜度判断流程
缓存对象存在
│
▼
检查是否过期?
│
├── 未过期 → 直接返回缓存(HIT)
│
▼
向源站发送条件请求
(If-Modified-Since / If-None-Match)
│
├── 304 Not Modified → 更新过期时间,返回缓存(REVALIDATED)
│
├── 200 OK → 更新缓存,返回新内容(REFRESH_HIT / MISS)
│
└── 源站不可达 → 返回过期缓存(STALE_HIT,如有配置)
6.3.2 条件请求 (Conditional Requests)
# 使用 Last-Modified 验证
# Squid 自动添加 If-Modified-Since 头
# 使用 ETag 验证
# Squid 自动处理 ETag 机制
# 将 reload 请求转为条件请求(节省带宽)
refresh_pattern . 60 20% 1440 reload-into-ims
# 忽略客户端的强制刷新
refresh_pattern . 60 20% 1440 ignore-reload
6.3.3 过期策略计算
Squid 计算缓存对象的过期时间的算法:
1. 检查源站的 Cache-Control: max-age=N
→ 过期时间 = 响应时间 + N 秒
2. 检查源站的 Expires 头
→ 过期时间 = Expires 时间
3. 使用 refresh_pattern 的启发式算法:
→ 过期时间 = 响应时间 + (当前时间 - Last-Modified) × percent
→ 限制在 min 和 max 之间
4. 如果没有任何信息,使用兜底 refresh_pattern
6.4 缓存控制
6.4.1 强制缓存特定内容
# 允许缓存带 Set-Cookie 的响应(默认不缓存)
# 谨慎使用,可能导致用户会话泄露
# reply_header_access Set-Cookie deny all
# 缓存带有 Vary 头的响应
# Squid 默认按 Accept-Encoding 缓存不同版本
# 忽略源站的 no-store 指令(危险,仅特殊场景)
# cache_store_log stdio:/var/log/squid/store.log
# reply_header_access Cache-Control deny all
6.4.2 禁止缓存特定内容
# 禁止缓存特定域名
acl no_cache_domain dstdomain .internal.example.com
cache deny no_cache_domain
# 禁止缓存特定 URL
acl no_cache_url url_regex ^/admin/
cache deny no_cache_url
# 禁止缓存认证用户的内容
acl authenticated proxy_auth REQUIRED
cache deny authenticated
# 禁止缓存 POST 请求
acl post_req method POST
cache deny post_req
# 禁止缓存特定响应码
# (Squid 默认不缓存非 200/301/302 的响应)
6.4.3 缓存键 (Cache Key) 自定义
# 默认缓存键基于 URL
# 可以通过 URL 重写改变缓存键
# 忽略 URL 中的查询参数(提高缓存命中率)
# 谨慎使用,可能导致不同内容被合并
# url_rewrite_program /usr/lib/squid/strip_query_args
# 基于 Host + Path 的缓存键(默认行为)
# Squid 使用完整的 URL 作为缓存键
6.5 缓存刷新
6.5.1 手动清除缓存
# 在配置中启用 purge 方法
acl PURGE method PURGE
acl purge_ips src 127.0.0.1 ::1 192.168.1.0/24
http_access allow PURGE purge_ips
http_access deny PURGE
# 清除单个 URL 的缓存
squidclient -h localhost -m PURGE http://example.com/image.jpg
# 清除整个域名的缓存(需要脚本)
# 获取缓存中的 URL 列表
grep "example.com" /var/log/squid/access.log | \
grep TCP_HIT | awk '{print $7}' | sort -u | \
while read url; do
squidclient -h localhost -m PURGE "$url"
done
6.5.2 清除所有缓存
# 方法一:删除缓存目录并重建
sudo systemctl stop squid
sudo rm -rf /var/spool/squid/*
sudo squid -z
sudo systemctl start squid
# 方法二:使用 cachemgr 清除
squidclient -h localhost mgr:shutdown
# 然后删除缓存目录并重启
6.5.3 缓存内容查询
# 查看缓存对象信息
squidclient -h localhost -p 3128 mgr:objects
# 查看缓存统计
squidclient -h localhost mgr:info | grep -i cache
# 查看缓存中的 URL
squidclient -h localhost mgr:storedir
# 使用 store.log 查看缓存操作
tail -f /var/log/squid/store.log
6.6 分层缓存 (Cache Hierarchy)
6.6.1 父代理缓存
# 定义父代理(上级缓存)
cache_peer parent.cache.example.com parent 3128 3130 \
no-query \
default \
login=user:password
# 父代理认证
cache_peer_access parent.cache allow all
# 强制通过父代理
never_direct allow all
# 可选:直接连接失败时才使用父代理
# prefer_direct on
# never_direct allow all
6.6.2 同级缓存 (Sibling Cache)
# 定义同级缓存
cache_peer sibling1.example.com sibling 3128 3130 \
proxy-only
cache_peer sibling2.example.com sibling 3128 3130 \
proxy-only
# 仅当同级缓存有内容时才请求(Digest 查询)
# 同级缓存之间通过 ICP/HTCP 协议查询
cache_peer_access sibling1 allow all
cache_peer_access sibling2 allow all
# 可选:启用 Cache Digest(减少查询开销)
# cache_peer sibling1.example.com sibling 3128 3130 proxy-only digest-url=sibling1:3128/squid-internal-periodic/digest
6.6.3 多级缓存架构
┌──────────────────────────────────────────────────┐
│ 三级缓存架构 │
│ │
│ ┌──────────────────┐ │
│ │ CDN 边缘节点 │ (Squid 反向代理) │
│ │ L1 缓存 │ │
│ └────────┬─────────┘ │
│ │ 回源 │
│ ┌────────▼─────────┐ │
│ │ 区域缓存节点 │ (Squid 父代理) │
│ │ L2 缓存 │ │
│ └────────┬─────────┘ │
│ │ 回源 │
│ ┌────────▼─────────┐ │
│ │ 源站服务器 │ (Origin Server) │
│ └──────────────────┘ │
└──────────────────────────────────────────────────┘
6.6.4 ICP 协议配置
# 启用 ICP(Internet Cache Protocol)
icp_port 3130
# ICP 访问控制
icp_access allow localnet
icp_access deny all
# 查询超时
icp_query_timeout 2 second
# HTCP 协议(更现代)
htcp_port 4827
htcp_access allow localnet
htcp_access deny all
6.6.5 Cache Digest
# 启用 Cache Digest(减少 ICP 查询)
digest_generation on
# Digest 大小限制
# cache_peer sibling.example.com sibling 3128 3130 \
# proxy-only digest-url=sibling:3128/squid-internal-periodic/digest
6.7 缓存命中率优化
6.7.1 提高命中率的策略
| 策略 | 配置/方法 | 效果 |
|---|---|---|
| 增大缓存空间 | 更大的 cache_dir | ★★★★ |
| 优化 refresh_pattern | 合理设置 min/max | ★★★★★ |
| 忽略不必要的头部 | override-expire | ★★★ |
| URL 规范化 | 移除追踪参数 | ★★★★ |
| 缓存更多类型 | 扩展文件类型列表 | ★★★ |
| 增加内存缓存 | 更大的 cache_mem | ★★★★ |
| 使用 GDSF 策略 | cache_replacement_policy heap GDSF | ★★★ |
6.7.2 监控命中率
# 查看缓存命中率统计
squidclient -h localhost mgr:info | grep -E "hit|miss|ratio"
# 输出示例:
# Request Hit Ratios: 5min: 72.3%, 60min: 68.5%
# Byte Hit Ratios: 5min: 45.2%, 60min: 42.1%
# 说明:Request 命中率高但 Byte 命中率低,说明小对象缓存效果好
# 大对象(如视频)可能未被缓存
# 查看详细缓存统计
squidclient -h localhost mgr:5min
# 查看单个对象的缓存状态
curl -x http://localhost:3128 -I http://example.com/image.jpg 2>&1 | grep -i "x-cache\|age\|hit"
6.7.3 命中率指标解读
| 指标 | 说明 | 健康范围 |
|---|---|---|
| Request Hit Ratio | 请求命中率 | > 60% |
| Byte Hit Ratio | 字节命中率 | > 30% |
| Memory Hit Ratio | 内存命中率 | > 50% |
| Swap Hit Ratio | 磁盘命中率 | > 40% |
注意:如果 Request Hit Ratio 高但 Byte Hit Ratio 低,说明小对象被有效缓存但大对象(视频、文件下载)频繁回源。可以考虑增大
maximum_object_size或使用分层存储。
6.8 缓存预热
6.8.1 预加载热点内容
#!/bin/bash
# cache_warmup.sh — 缓存预热脚本
# 热点 URL 列表
URLS=(
"http://example.com/static/css/main.css"
"http://example.com/static/js/app.js"
"http://example.com/static/images/logo.png"
"http://example.com/static/images/banner.jpg"
)
for url in "${URLS[@]}"; do
echo "Warming up: $url"
curl -x http://localhost:3128 -s -o /dev/null "$url"
done
echo "Cache warmup completed."
6.8.2 从日志生成预热列表
# 提取最近 7 天访问量最高的 URL
awk '{print $7}' /var/log/squid/access.log | \
sort | uniq -c | sort -rn | head -100 | \
awk '{print $2}' > /tmp/hot_urls.txt
# 逐个预热
while read url; do
curl -x http://localhost:3128 -s -o /dev/null "$url"
done < /tmp/hot_urls.txt
6.9 缓存故障与恢复
6.9.1 缓存损坏修复
# 检查缓存目录完整性
sudo squid -k shutdown
sudo rm -f /var/spool/squid/swap.state
sudo squid -z
sudo squid -s
# 如果缓存目录严重损坏
sudo systemctl stop squid
sudo rm -rf /var/spool/squid/*
sudo squid -z
sudo systemctl start squid
6.9.2 缓存迁移
# 停止服务
sudo systemctl stop squid
# 备份缓存目录
sudo tar czf /backup/squid-cache-$(date +%Y%m%d).tar.gz /var/spool/squid/
# 移动到新磁盘
sudo mv /var/spool/squid /mnt/newdisk/squid
sudo ln -s /mnt/newdisk/squid /var/spool/squid
# 确保权限正确
sudo chown -R proxy:proxy /mnt/newdisk/squid
# 重启
sudo systemctl start squid
6.10 本章小结
| 功能 | 关键配置 |
|---|---|
| 缓存目录 | cache_dir (ufs/aufs/rock) |
| 内存缓存 | cache_mem + maximum_object_size_in_memory |
| 缓存策略 | refresh_pattern (min/percent/max) |
| 缓存控制 | cache allow/deny |
| 缓存刷新 | PURGE 方法 + 手动清理 |
| 分层缓存 | cache_peer + ICP/HTCP |
| 替换策略 | cache_replacement_policy heap GDSF |