用 Redis 给 VPS 上的网站/API 加速:缓存模式与避坑

数据库前面加一层 Redis,响应能快一个数量级。讲清缓存读写模式、过期策略、以及缓存穿透/雪崩这些经典坑。

同样的查询,直接打 PostgreSQL 可能要几十毫秒,前面垫一层 Redis 往往能压到亚毫秒——差的就是一个数量级。很多 VPS 上的网站越跑越慢,不是代码烂,而是每一次请求都把同样的脏活累活丢给数据库:热门文章、商品详情、用户资料、API 计数,反反复复查。Redis 是内存里的键值存储,把这些「算过一次就不该再算」的结果缓存起来,数据库瞬间就闲了,响应也快了。但缓存不是加了就万事大吉,用错了反而会引入「缓存穿透 / 击穿 / 雪崩」三大经典故障,把数据库一波带走。本文按「模式 → 过期 → 三大坑 → 持久化」的顺序,把 VPS 上加 Redis 的关键点讲透,全部给可照搬的配置和命令。

延伸阅读

更多相关攻略推荐:CPU VPS 被边缘化?一块 H100 卖 25 万,GPU 云如2026 游戏服 VPS 怎么选:高主频 CPU、内存与 DDoS 2026 高主频 CPU VPS:单核性能、建站与游戏服的隐藏指标用几台便宜 VPS 组 k3s / Docker Swarm 容器集2026 存储型/大硬盘 VPS:备份、网盘与冷数据的性价比之选

一、为什么数据库前面加一层 Redis 能快一个数量级

根本原因在于存储介质的差距。PostgreSQL 的数据在磁盘上,一次查询要走索引、读页、可能还要排序聚合,再快也是毫秒级且受磁盘 I/O 和连接数限制。Redis 把数据放在内存里,读写的延迟是亚毫秒级,单实例轻松扛每秒十万甚至上百万次操作。当你的接口里有一类「读多写少、计算贵」的数据——比如首页聚合、商品详情、排行榜、会话信息——把它们放进 Redis,请求在 Redis 这一层就被消化掉,根本到不了数据库。

但要注意 Redis 的定位:它是缓存,不是数据库替代品。它的一切优势都建立在「数据在内存里」这个前提上,所以内存就是它的容量上限,不是磁盘。需要强一致、需要复杂联表查询、需要长期留存的数据,还是老老实实放 PostgreSQL。Redis 负责「加速」,数据库负责「真相」。下面讲的几种模式,本质上都是在处理「缓存和数据库如何协作」这件事。

二、缓存读写模式:Cache-Aside 与 Read-Through

最常用的是 Cache-Aside(旁路缓存,也叫懒加载):读的时候先查缓存,命中就直接返回;没命中再去查数据库,把结果写回缓存并设一个过期时间,然后返回。这个模式简单、缓存是「可选层」——就算 Redis 挂了,应用还能直接查数据库活下来,真相来源始终是数据库。它的代价是失效问题:数据更新后,缓存里是旧的,直到 TTL 过期或主动失效。所以写操作的正确姿势是「先更新数据库,再删除缓存」(DEL 那个 key),而不是去更新缓存的值——因为并发下更新缓存可能产生不一致,删掉让它下次读时重建更稳妥。

# Cache-Aside 读路径示意
GET user:42
# 未命中
SET user:42 "..." EX 300     # 写回,5 分钟 TTL
# 下次命中直接返回

Read-Through(读穿透)则是把「未命中时去数据库加载」这一步交给缓存层/缓存库自己去透明完成,应用只管调缓存,代码更简洁但控制力弱一些。在纯 Redis 部署里,Cache-Aside 用得最多,加载逻辑写在应用代码里。

另外两个模式了解即可:Write-Through(写穿透)是写的时候同时写缓存和数据库,缓存永远新鲜,代价是写变慢、且会缓存一些可能根本没人读的数据;Write-Behind(写回)是先写缓存就返回,后台异步刷回数据库,写极快,但 Redis 在刷盘前挂了就会丢数据,对「数据库才是真相」的系统是个隐患。绝大多数 Web 服务的起点都是 Cache-Aside + TTL,够用且稳。

三、TTL 与 maxmemory 淘汰策略

第一条铁律:永远给缓存 key 设 TTL。一个没设过期的 key,会一直活到你记得删它为止,或者等到撞上 maxmemory 被不可预期地挤掉。设置方式很简单:

SET student:42 "..." EX 300      # 写入时给 5 分钟
EXPIRE student:42 300            # 给已有 key 加过期
TTL student:42                   # 查剩余秒数
PERSIST student:42               # 去掉过期(一般不该这么做)

当 Redis 用到 maxmemory 上限时,靠 淘汰策略(maxmemory-policy)决定扔哪些 key。常见选项:

  • noeviction:写满了直接拒绝写入。某些默认配置会这样,做缓存时不想要。
  • allkeys-lru:从所有 key 里淘汰最久没被访问的(LRU)。纯缓存的经典选择。
  • allkeys-lfu:淘汰访问频率最低的(LFU)。当访问模式倾斜、最近访问会骗人时更合适。
  • volatile-lru / volatile-lfu:只淘汰「带过期时间」的 key。适合缓存和会话混放的场景。
  • volatile-ttl:优先淘汰最快过期的 key。

对纯缓存,首选 allkeys-lru 或 allkeys-lfu。maxmemory 一般设到可用内存的约 75%,给 BGSAVE 时的写时复制(COW)留出余量。在 redis.conf 里:

maxmemory 4gb
maxmemory-policy allkeys-lru
maxmemory-samples 10            # 越大 LRU 估算越准(默认 5)

上线后盯两个指标:命中率(redis-cli INFO stats 里的 keyspace_hits 和 keyspace_misses,命中率目标 80% 以上)和 evicted_keys(INFO stats 里,如果 key 被意外大量淘汰,说明缓存太小,该扩容了)。

四、三大经典坑:穿透 / 击穿 / 雪崩

缓存真正危险的地方不是慢,而是「缓存失效的瞬间海量请求直达数据库」。按现象分三种,对策完全不同。

缓存穿透(查不存在的数据):攻击者或用错参数反复查询数据库里根本不存在的 ID,缓存没命中,请求全部打到数据库,QPS 暴涨把库拖垮。对策有二:一是布隆过滤器(RedisBloom)前置一道屏障,把所有合法 ID 哈希进位数组,判断不存在就直接返回,连缓存和库都不碰;二是缓存空值——查到是空也写进 Redis 并给个短过期(比如 60 秒),挡住同一个非法 key 反复穿透。两条一起上最稳。

缓存击穿(热点 key 过期瞬间):某个爆款商品、首页热点 key 在过期的一瞬间,大量并发同时发现缓存失效,一拥而上打数据库,瞬间压垮连接。注意它和穿透的区别:击穿的数据是真实存在的,只是恰好过期。对策:互斥锁——缓存失效时用 SETNX 只放一个线程去重建缓存,其余线程等待重试;或者逻辑过期——不给 key 设物理过期,把过期时间存在 value 里,发现逻辑过期后异步起线程更新,期间继续返回旧值保可用。

缓存雪崩(大量 key 同时失效):批量导入缓存时如果给所有 key 设了相同的过期时间,到点后海量 key 一起失效,请求集体击穿缓存,数据库瞬间满载甚至整体崩溃。对策:过期时间加随机偏移,比如基础 TTL 1 小时加 0 到 300 秒的随机数,把失效时间打散;再上多级缓存(本地 Caffeine 作一级、Redis 作二级),即使 Redis 大规模过期本地还能挡一阵;Redis 自身用集群 / 哨兵保证高可用,避免单点宕机整体失效;服务层再加限流熔断兜底。

五、持久化与内存占用

既然 Redis 是内存存储,就得想清楚「宕机后数据怎么办」和「内存到底占多少」。

持久化有两套机制,可以都开:RDB 是定时全量快照(例如 save 900 1、save 300 100、save 60 10000),文件紧凑、重启快、对正常操作几乎零影响,但两次快照之间可能丢几分钟数据,且大数据集 fork 时可能卡顿。AOF 把每条写命令追加记录,推荐 appendfsync everysec(每秒落盘一次,最多丢 1 秒),更耐用但文件更大、大文件重启更慢。Redis 4.0+ 支持混合持久化(aof-use-rdb-preamble),恢复速度最快。两个都开时,启动时以 AOF 为准,因为它通常更完整。

内存占用上,要记住两件反直觉的事:第一,Redis 做 RDB 或 AOF 重写时,因为写时复制,内存可能短暂涨到平时的约 2 倍,所以 maxmemory 别顶满,并给系统开 swap 兜底,避免 OOM 把 Redis 自己杀掉。第二,Linux 上建议设 vm.overcommit_memory = 1 并关掉透明大页(echo never > /sys/kernel/mm/transparent_hugepage/enabled),否则 fork 和内存开销会拖累延迟。生产环境还要做安全加固:bind 到 127.0.0.1 而不是 0.0.0.0、设 requirepass 强密码、用 rename-command 禁用 FLUSHDB / FLUSHALL / CONFIG 这类危险命令。

# 生产向 redis.conf 片段
bind 127.0.0.1 ::1
requirepass your-very-long-random-password
maxmemory 4gb
maxmemory-policy allkeys-lru
save 900 1
save 300 100
save 60 10000
appendonly yes
appendfsync everysec
rename-command FLUSHDB ""
rename-command FLUSHALL ""

延伸阅读:缓存加速和数据库备份是两件互补的事,别忘了 自动备份数据库到对象存储 这条保命链路;选机器时 VPS 新手入门指南 能帮你按内存规划缓存容量;想看具体厂商,RackNerd 评测 也值得参考它小内存机型的体感。