强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Squid 完全指南 / 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

扩展阅读