原子网络科技 原子网络科技

Redis在网站建设中常见的应用有:页面缓存、会话存储、限流和队列任务处理

a169kai 2026年11月04日 · 阅读约 5 分钟

四种场景对应四种数据结构、四组参数。缓存要处理穿透、击穿和雪崩的兜底,会话要处理一致性和过期策略,限流要处理原子性和窗口精度,队列要处理投递和失败重投。选型的关键不是命令本身,而是失效边界。

一个内容型站点把首页从数据库直查改为缓存渲染之后,接口响应明显变好了,但是第三天整点的时候数据库连接被打满了。排查后发现上千个键在同一秒设置了相同的过期时间,失效的时候全部回源。不是Redis的性能问题,而是过期策略没有打散,降级逻辑也没有准备。同类的问题在项目复盘时反复出现:缓存改造的收益在第一天就兑现了,风险在第七天才暴露出来,两者的差距完全在于细节设计。

从技术角度分析的话,按照四种不同的场景来拆分,选择的路径是比较清楚的。页面缓存一般用 String 来存储整个渲染结果,键名带有模板版本号以便于整体失效,TTL 叠加随机抖动来防止集体过期;热点键采用逻辑过期,由后台线程异步重建,请求侧不阻塞;查询结果为空的时候缓存一个短 TTL 的占位值,配合布隆过滤器在入口拦截,减少无效穿透;内存上限设为 maxmemory,淘汰策略为 allkeys-lru 或者 volatile-lru,视乎是否允许淘汰没有过期时间的键。会话存储更适合 Hash,字段级读写、整体续期,滑动过期用每次访问刷新 TTL 实现;多实例部署时必须要保证同一个会话落到同一个分片上,或者把会话完全托管给 Redis,避免本地内存和集中存储双写不一致。限流使用 INCR 加 EXPIRE 的固定窗口实现比较简单,但是窗口边界会有突刺;如果要平滑的话就改用 ZSet 记录时间戳来做滑动窗口,或者按照令牌桶的方式在 Lua 脚本里完成取令牌和写回,脚本的单线程执行保证了原子性,省去了分布式锁。队列任务分为两种:List 的 LPUSH 配 BRPOP 可以应付日志、通知等场景,但是没有确认机制,进程崩溃就会丢失消息;Stream 的消费者组提供了 pending 列表和 XACK 确认,配合 XAUTOCLAIM 回收超时未确认的消息,投递语义为至少一次,消费端需要幂等键去重。延迟任务用 ZSet 按 score 排序轮询,到点搬运到 Stream 或者直接执行。

判断这些设计是否成立,依据的是命令的确定语义,不是厂商宣传。EXPIRE 采用惰性删除和定期抽样删除的方式,过期键不能保证立即释放内存;TTL 精度受到内部时钟粒度的限制。Stream 的消费者组提供的是至少一次投递,重复消费由业务侧处理。持久化方面,RDB 按时间点快照,AOF 按写入追加,两者的落盘窗口决定了故障时刻可能丢失的数据范围。这些行为在官方文档和命令说明里都有明确的定义,可以逐条对照,不依靠经验口径。

边界也要写清楚。Redis 是加速层,不是数据库的替代品;缓存和数据库之间的一致性依靠失效策略以及重试机制来保证,而不是依靠事务来保证。持久化存在丢失窗口,所以不能丢的任务要先落库再入队。哨兵和集群可以解决单点故障和脑裂的问题,但是会带来新的运维复杂度和延迟问题。内存单价比磁盘高,缓存大对象之前先按序列化后体积估算占用。把这些限制写在方案里面,比在故障之后补文档要好得多。

总结:

四种场景共同作用的结果就是把不确定性挡在数据库前面,代价是牺牲了一致性并增加了运维复杂度。缓存影响读性能,会话影响登录体验,限流影响系统在高峰期的存活率,队列影响异步任务的可靠性边界。参数和语义写清楚,Redis 才是加速器;写成黑盒,就是新的故障源。

FAQ:

Q1: 页面缓存与CDN如何分工?

CDN 用于处理静态资源和可以长期缓存的页面,边缘节点就近返回。Redis 处理的是根据登录状态、地理位置以及参数来组装的片段。分层原则就是越靠近前面的层级缓存的内容就越静态,回源次数由内层来决定。

Q2:缓存穿透、缓存击穿、缓存雪崩的区别是什么?

穿透就是查询不存在的数据,请求一直打到数据库上,用空值缓存加上布隆过滤器来拦截。击穿就是单个热点键失效的时候被高并发回源,用逻辑过期或者互斥重建。雪崩就是大量的键同时失效,用TTL随机抖动和多级缓存来缓解。

Q3:会话存在Redis中还是使用JWT?

需要即时踢下线、权限实时变更、服务端可审计,选 Redis 会话。追求无状态横向扩展、跨域调用、减少一次存储往返,选 JWT,但是要接受不能主动失效的代价,一般配合短有效期和刷新令牌使用。

Q4:限流为什么建议放在 Lua 脚本里?

取计数、判断阈值、写回这三个步骤如果分多次往返的话,中间存在并发窗口,那么计数就会出错。Lua 脚本在 Redis 中单线程运行,三步合为一个原子操作,减少了网络往返次数。

Q5:轻量队列选择List还是Stream?

只要求先进先出、可以丢弃消息、实现越简单越好,选择List。需要消费确认、失败重投、多消费组、堆积监控,选择Stream。判断标准是任务丢失是否会影响业务,而不是性能好坏。

0 0 投票数
文章评分
订阅评论
提醒
guest
0 评论
最旧
最新 最多投票