为您的应用使用 Redis:模式、陷阱与实用建议
了解在应用中使用 Redis 的实用方法:缓存、会话、队列、pub/sub、速率限制,以及扩展、持久化、监控和常见陷阱。

Redis 为现代应用带来的作用
Redis 是一个内存数据存储,经常被用作应用之间共享的“快速层”。团队喜欢它,因为上手简单、对常见操作极快,并且灵活得足以承担多种任务(缓存、会话、计数器、队列、pub/sub),无需为每一种工作引入新的系统。
在实践中,最好把 Redis 看作是加速 + 协调,而把你的主数据库保留为事实来源(source of truth)。
Redis 在典型架构中的位置
一个常见的设置如下:
- 数据库:耐久且具权威性的数据(订单、用户、发票)
- Redis:快速访问与共享短暂状态(缓存页面、会话令牌、速率限制计数器)
- 应用:决定数据放哪里,何时刷新、失效或重建
这种划分让数据库专注于正确性与耐久性,而把高频读写交给 Redis 吸收,避免数据库成为延迟或负载的瓶颈。
使用 Redis 通常能得到的收益
合理使用时,Redis 会带来若干实际效果:
- 更快的读取:优先从内存返回频繁请求的数据,而不是每次都访问数据库。\n- 更平滑的流量突发:缓存和轻量计数器帮助你承受突发流量,而不至于数据库崩溃。\n- 更简单的协调:多台应用服务器可以共享短暂状态(会话、锁、去重键),无需每台机器重复构建该逻辑。
什么时候 Redis 不适合
Redis 不是主数据库的替代品。如果你需要复杂查询、长期存储保证或用于分析/报表,主数据库仍是合适之处。
同样,不要默认认为 Redis 就是“持久化的”。如果丢失几秒钟的数据不可接受,你需要基于真实的恢复要求仔细配置持久化,否则应考虑其他系统。
实施前应了解的 Redis 基本概念
常有人把 Redis 说成“键值存储”,但更有用的看法是把它看作一个非常快速的服务器,可以按名字(键)存放和操作小块数据。这种模型鼓励可预测的访问模式:你通常明确知道想要什么(会话、缓存页面、计数器),Redis 能在一次往返中获取或更新它。
快速的原因:以内存为先
Redis 把数据保存在 RAM 中,这就是它能在微秒到低毫秒级响应的原因。代价是 RAM 有限且比磁盘更昂贵。
早期就要决定 Redis 是:
- 仅作为性能层(纯缓存),还是\n- 处于状态路径中(会话、队列),此时重启行为和持久化设置很重要
Redis 可以把数据持久化到磁盘(RDB 快照和/或 AOF 追加日志),但持久化会增加写入开销并迫使你在耐久性上做出权衡(例如“更快但可能丢失一秒” vs “更慢但更安全”)。把持久化当作一个根据业务影响调节的旋钮,而不是一个自动勾选的框。
单线程并不等于慢
Redis 大部分命令在单线程中执行,这听起来像个限制,但有两点需要记住:操作通常都很小,而且不存在多个工作线程之间的锁开销。只要避免昂贵命令和过大的负载,这个模型在高并发下可以非常高效。
客户端、连接与请求模式
你的应用通过 TCP 使用客户端库与 Redis 通信。使用连接池,保持请求尽量小,并在需要多次操作时优先使用批量/流水线。
为超时和重试做规划:Redis 很快,但网络不是。应用应在 Redis 忙或临时不可用时优雅降级。
如果你正在构建新服务并想快速统一这些基础配置,像 Koder.ai 这样的平台可以帮助你搭建 React + Go + PostgreSQL 的脚手架,然后通过对话式工作流添加基于 Redis 的功能(缓存、会话、速率限制),同时允许导出源码并在任意环境运行。
在实际应用中可行的缓存模式
缓存只有在其拥有权清晰时才有用:谁来填充、谁来失效、以及“足够新”的定义是什么。
cache-aside 模式(大多数应用的默认选择)
cache-aside 意味着你的应用而非 Redis 控制读写。
典型流程:
- 读取:先在 Redis 查找该项。\n2. 命中:立即返回。\n3. 未命中:从主数据源(数据库、API、服务)获取。\n4. 填充:带 TTL 把结果存入 Redis。\n5. 返回:响应调用方。
Redis 是一个快速的键值存储;你的应用决定如何序列化、版本化和过期条目。
TTL:如何选择过期时间且不让用户惊讶
TTL 既是产品决策也是技术决策。短 TTL 降低陈旧风险但会增加数据库负载;长 TTL 节省工作量但可能返回过时结果。
实践建议:
- 匹配数据的自然刷新频率(例如价格 vs 头像)。\n- 使用版本化键 处理“模式变更”(例如
user:v3:123),避免旧缓存格式破坏新代码。\n- 有意处理陈旧数据:某些视图允许轻微陈旧;对于库存或认证这类场景则不能。
避免缓存雪崩
当一个热点键过期时,可能会有大量请求同时未命中。
常见防御:
- 请求合并:只让一个请求重建值,其他请求等待或返回旧值。\n- TTL 抖动:添加小的随机值,避免大量键同时过期。\n- 软 TTL:把值短暂地视为“可用但已陈旧”,后台异步刷新 Redis。
缓存什么、跳过什么
好候选包括 API 响应、昂贵查询结果 和 计算后对象(推荐、聚合)。缓存完整 HTML 页面可行,但要小心个性化和权限——在有用户特定逻辑时优先缓存片段。
会话存储与认证工作流
Redis 是存放短期登录状态的实用之地:会话 ID、刷新令牌元数据、“记住此设备”标记。目标是让认证快速,同时对会话生命周期和撤销进行严格控制。
在 Redis 中使用用户会话
常见模式:应用生成随机会话 ID,把精简记录存 Redis,然后把 ID 作为 HTTP-only cookie 返回浏览器。每次请求时查找会话键并把用户身份与权限附加到请求上下文。
Redis 在这里很适合,因为会话读取频繁且内置了过期机制。
键设计与 TTL 管理
将键设计得便于扫描与撤销:
sess:{sessionId}→ 会话载荷(userId、issuedAt、deviceId)\n-user:sessions:{userId}→ 活跃会话 ID 的 Set(可选,用于“注销所有设备”)
对 sess:{sessionId} 使用与会话生命周期相匹配的 TTL。建议旋转会话时(推荐)创建新会话 ID 并立即删除旧的。
对“滑动过期”要小心(在每次请求时延长 TTL):这会让高频用户的会话无限期延续。较为安全的折中是仅在接近过期时才延长 TTL。
撤销与跨设备登出
要登出单个设备,删除 sess:{sessionId}。
要跨设备登出,可以:
- 删除
user:sessions:{userId}中所有列出的会话 ID,或\n- 保持user:revoked_after:{userId}的时间戳,把该时间戳之前签发的所有会话视为无效
使用时间戳的方法可以避免大量并发删除操作。
隐私与安全考虑
在 Redis 中只存最少必要的信息——优先存 ID 而不是个人数据。绝不存原始密码或长期秘密。如果必须存储与令牌相关的数据,存储哈希值并使用严格的 TTL。
限制能连接到 Redis 的主体、启用鉴权,并确保会话 ID 具有高熵以防暴力猜测攻击。
速率限制与滥用防护
速率限制是 Redis 的强项:它快速、在应用实例间共享,并提供原子操作,能在高流量下保持计数一致性。适用于保护登录端点、昂贵搜索、密码重置流及任何可能被抓取或暴力破解的 API。
常见的速率限制模型
固定窗口:最简单,“每分钟 100 次请求”。你统计当前分钟桶内的请求数。容易实现,但在边界可能产生突发(例如 12:00:59 有 100 次,12:01:00 又有 100 次)。
滑动窗口:通过查看过去 N 秒/分钟内的请求来平滑边界。更公平,但通常成本更高(可能需要有序集合或更多账本处理)。
令牌桶:适合处理突发。用户随时间“获得”令牌,上限有限;每次请求消耗一个令牌。允许短时间突发,同时约束平均速率。
安全构建块:INCR/EXPIRE 与原子性
一个常见的固定窗口模式是:
- 使用
INCR key增加计数\n- 使用EXPIRE key window_seconds设置/重置 TTL
关键在于做到安全。如果把 INCR 和 EXPIRE 当成独立调用,二者之间崩溃可能导致键永不过期。
更安全的方法包括:
- 使用 Lua 脚本同时执行
INCR并仅在计数器首次创建时设置EXPIRE。\n- 或使用SET key 1 EX <ttl> NX进行初始化,然后再INCR(通常仍把它们包在脚本里以避免竞态)。
在流量突增时,原子操作尤其重要:没有原子性,两次请求可能看到相同的剩余配额并都通过。
作用域:按用户、按 IP、按路由(以及突发处理)
大多数应用需要多层限制:
- 按用户 的限制用于已验证调用(例如
rl:user:{userId}:{route})\n- 按 IP 的限制用于匿名或认证前端点(例如登录尝试)\n- 按路由 的限制用于保护热点(搜索、导出、报表)
对于突发流量端点,令牌桶(或一个较宽松的固定窗口加上短期“突发”窗口)有助于避免误伤合法突发(如页面加载或移动端重连)。
Redis 不可用时:放行还是拒绝
事先决定“安全”的含义:
- Fail-open(放行):当 Redis 无法访问时允许请求。更高可用性和更好用户体验,但滥用防护变弱。\n- Fail-closed(拒绝):当 Redis 不可用时拒绝请求。防护强,但可能导致你的应用部分下线。
常见折中是对低风险路由采用 fail-open,对敏感路由(登录、密码重置、OTP)采用 fail-closed,并通过监控在速率限制停止工作时及时发现问题。
使用 Redis 做队列与后台任务
在需要轻量队列来发送邮件、调整图片大小、同步数据或运行定期任务时,Redis 可以驱动后台作业。关键是选择合适的数据结构并对重试与失败处理设定明确规则。
列表、有序集合与 Streams:何时使用哪种
Lists 是最简单的队列:生产者 LPUSH,工作进程 BRPOP。简单但需要额外逻辑来处理“在途”任务、重试和可见性超时。
Sorted sets 在调度场景中很有优势。用 score 表示时间戳(或优先级),工作进程获取最早到期的任务,适合延迟任务和优先级队列。
Streams 通常是耐用工作分发的最佳默认选项。它们支持 consumer groups、保留历史并允许多个工作进程协调,无需自己实现“处理中队列”。
确认、重试与死信处理
使用 Streams consumer groups 时,工作进程读取消息并在处理后 ACK 它。如果某个工作进程崩溃,消息会保持为 pending,可以被其他工作进程认领。
对于 重试,在消息负载或侧键中跟踪尝试次数,并采用指数退避(通常通过有序集合实现“重试计划”)。超过最大尝试次数后,将作业移到 死信队列(另一个 stream 或 list)供人工查看。
工作进程的幂等策略
假设作业可能被执行多次。通过以下方式让处理程序幂等:
- 使用幂等键(例如
job:{id}:done),在做副作用前用SET ... NX先写入。\n- 设计操作为 upsert,而不是“盲目创建”。\n- 在调用第三方 API 时记录外部请求 ID。
保持作业小并使用背压
保持负载小(把大数据放外部存储并传引用)。通过限制队列长度、在滞后增加时放慢生产者,以及根据待处理深度与处理时间按需扩展工作进程来实现背压。
Pub/Sub 消息与事件分发
Redis Pub/Sub 是最简单的广播事件方式:发布者向频道发送消息,所有连接的订阅者都会立即收到。无需轮询——就是一种轻量的“推送”,适合实时更新场景。
适合 Pub/Sub 的常见用途
当你更在意速度与扇出而不是保证交付时,Pub/Sub 很合适:
- 面向用户的通知(“你的报告已就绪”)\n- 实时 UI 更新(在线/离线、正在输入指示、仪表盘)\n- 内部事件扇出(一个事件触发多个服务)
一个有用的比喻:Pub/Sub 像电台广播。任何正在收听的人都会收到,但没人能自动获得录音。
需要计划的关键限制
Pub/Sub 有重要的权衡:
- 无持久化:如果发布时没人订阅,消息就丢失。\n- 订阅者可靠性:如果订阅者断连或过载,会丢失消息。\n- 无重放与无确认:不能要求 Redis “直到确认为止”地重传。
因此,Pub/Sub 不适合需要每条事件都被处理的工作流(无论是“至少一次”还是“恰好一次”)。
何时优先使用 Redis Streams
如果你需要 耐久化、重试、consumer groups 或 背压处理,Redis Streams 通常是更合适的选择。Streams 允许你存储事件、用确认处理它们并在重启后恢复——更接近轻量消息队列。
多实例应用的模式
在真实部署中会有多个应用实例订阅。实用建议:
- 命名空间频道 以避免冲突:
app:{env}:{domain}:{event}(例如shop:prod:orders:created)。\n- 区分广播与定向频道:广播用notifications:global,按用户目标用notifications:user:{id}。\n- 保持负载小且自包含:包含 ID 与最少元数据,只有在必要时再去其他地方拉取详情。
这样使用时,Pub/Sub 是快速的事件“信号”,而 Streams(或其他队列)负责那些不能丢失的事件处理。
选择合适的 Redis 数据结构
选数据结构不仅关乎“能否工作”——它影响内存使用、查询速度以及代码长期的简洁度。一个好规则是选择符合你以后会提出的问题(读取模式)的结构,而不仅仅是当前如何存储数据。
快速选择指南(strings、hashes、sets、sorted sets)
- Strings:适合单一值(JSON blob、功能开关、缓存的 HTML)。也适合用
INCR/DECR做原子计数器。\n- Hashes:适合“一个对象多字段”(用户资料字段、购物车总额)。当你经常更新单个属性时很理想。\n- Sets:适合唯一性和成员检查(用户是否已领取优惠券 X?)。SISMEMBER快且集合操作方便。\n- Sorted sets (ZSETs):适合排名数据与“Top N”查询(排行榜、优先列表、基于时间的评分)。
原子更新、计数器与排行榜
Redis 的命令在单个命令层面是原子的,因此可以安全地做计数递增而不会出现竞态。页面浏览量和速率限制计数通常使用带过期的字符串和 INCR。
排行榜场景下 sorted sets 非常合适:可用 ZINCRBY 更新分数,用 ZREVRANGE 高效获取排名前 N,而无需扫描全部条目。
使用 hashes 降低键数量并改善组织
如果你为同一用户创建很多键(user:123:name、user:123:email、user:123:plan),会增加每个键的元开销并使键管理更困难。
像 user:123 这样的 hash,带字段 name、email、plan,可以把相关数据聚合在一起,通常更省内存,也便于做部分更新(只改一个字段而不是重写整个 JSON)。
影响账单的内存考量
- 大量小键 由于每键开销,可能比预期占用更多内存。\n- Hashes 对于小到中等大小的对象在一个键下存储通常更节省内存。\n- Sorted sets 很强大但比 sets/strings 更重——只有在确实需要排名或基于分数查询时才使用。
有疑问时,建模一小部分样本并测量内存使用,再决定是否将该结构用于高流量数据。
持久化、复制与数据安全
Redis 常被称为“内存数据库”,但你仍然可以选择节点重启、磁盘填满或服务器消失时的行为。合适的设置取决于你能接受丢失多少数据以及需要多快恢复。
RDB vs AOF:各自提供了什么
RDB 快照 保存数据集的某个时间点。它们紧凑且在启动时加载快,这能使重启更快。代价是你可能会丢失自上次快照以来的最近写入。
AOF(追加文件) 按写操作顺序记录日志。通常能减少潜在的数据丢失,因为更频繁地记录了更改。AOF 文件会更大,且启动时重放可能更久——不过 Redis 可以重写/压缩 AOF 以保持可控。
很多团队两者同时运行:快照用于更快的重启,AOF 用于更好的写入持久性。
持久化对延迟与重启的影响
持久化并非免费。磁盘写入、AOF fsync 策略和后台重写操作会在存储慢或饱和时引起延迟突增。另一方面,持久化会让重启不那么可怕:没有持久化,意外重启会导致 Redis 为空。
复制与故障切换目标
复制把数据复制到副本节点,以便主节点挂掉时进行故障切换。目标通常是优先可用性,而非完美一致性。在故障时,副本可能略微滞后,故障切换可能会丢失最后几次已确认的写入。
明确可接受的数据丢失和恢复时间
在调整之前写下两个数字:
- 可接受的数据丢失(RPO):“我们最多能丢失 X 秒/分钟的数据。”\n- 恢复时间(RTO):“我们必须在 Y 秒/分钟内恢复。”
用这些目标来选择 RDB 频率、AOF 设置,以及是否需要副本(和自动故障切换)来满足 Redis 的角色——缓存、会话、队列或主数据存储。
Redis 的扩展:从单实例到集群
单个 Redis 节点能支撑的工作量往往超出预期:它简单、易操作且对许多缓存、会话或队列工作负载已经足够快。
当你触及内存上限、CPU 饱和或单点失效无法接受时,就需要扩展了。
何时从单节点迁移到多节点
当以下任一情况成立时考虑增加节点:
- 数据集在 RAM 中已无足够安全余量。\n- 峰值流量时延迟因节点 CPU 饱和而激增。\n- 你需要比“重启恢复”更高的可用性。\n- 有多类工作负载竞争(例如缓存 + 队列),希望隔离它们。
一个实用的第一步通常是分离工作负载(启动两个独立的 Redis 实例),而不是直接上集群。
分片与 Redis Cluster 的通俗解释
分片是把键分散到多台 Redis 节点上,使每台节点只存部分数据。Redis Cluster 是 Redis 的内置自动分片机制:键空间分为槽,节点拥有其中的一部分。
收益是更多总内存与更高聚合吞吐量。代价是复杂度上升:多键操作受限(键必须在同一分片),排障也涉及更多环节。
热键与流量不均衡
即使分片看似均匀,实际流量也可能倾斜。单个热门键(热键)会压垮某个节点,而其它节点空闲。
缓解方法包括短 TTL 加抖动、把值拆分到多个键(键哈希化),或重新设计访问模式以分散读取。
客户端注意事项:集群感知驱动与路由
Redis Cluster 需要一个集群感知的客户端来发现拓扑、把请求路由到正确节点并在槽迁移时跟随重定向。
迁移前确认:
- 你的语言驱动完全支持 Redis Cluster。\n- 你的连接池策略适用于多节点场景。\n- 代码避免跨分片的多键命令(或者使用 hash tag 把相关键放在同一分片)。
扩展最好作为有规划的演进:用压测验证、监测键延迟并逐步迁移流量,而非一次性切换。
Redis 部署的安全要点
Redis 常被视为“内部设施”,正因如此它经常成为攻击目标:一个暴露的端口可能导致数据泄露或被攻击者控制的缓存。即便只存“临时”数据,也应把 Redis 当作敏感基础设施对待。
认证与访问控制
首先启用认证并使用 ACLs(Redis 6+)。ACL 可以:
- 为应用、工作进程和管理员创建独立用户\n- 限制命令(例如允许 GET/SET 但禁止 CONFIG)\n- 按前缀限制键(对多租户很有用)
避免把一个密码共享给所有组件。应为每个服务发放凭据并收窄权限。
网络隔离与 TLS
最有效的控制是不可访问。把 Redis 绑定到私有接口,放在私有子网,并用安全组/防火墙限制入站流量到仅需要访问的服务。
当 Redis 流量跨越你无法完全信任的主机边界(多可用区、共享网络、Kubernetes 节点或混合环境)时,使用 TLS。TLS 能防止嗅探与凭据被窃取,对会话、令牌或任何用户相关数据,其小额开销是值得的。
危险命令与误配置
锁定可能被滥用导致重大损害的命令。常见应禁用或通过 ACL 限制的命令包括:FLUSHALL、FLUSHDB、CONFIG、SAVE、DEBUG 和 EVAL(或至少对脚本使用严格控制)。虽然 rename-command 可用于隐藏危险命令,但 ACL 更清晰且易审计。
秘密处理与轮换
把 Redis 凭据存放于机密管理器(不要放在代码或镜像中),并规划轮换。轮换在客户端能在不重部署的情况下重新加载凭据时最容易实现,或在过渡窗口内支持两个有效凭据。
如果需要可操作的清单,把它放进你的运行手册,和 /blog/monitoring-troubleshooting-redis 一起维护。
监控、排障与运维规范
Redis 常常“看起来没问题”……直到流量变化、内存逐渐增长或某个慢命令把一切阻塞。轻量的监控流程与清晰的事故检查表能避免大多数意外。
真正重要的指标
从一小组可以向团队解释的指标开始:
- 已用内存 vs maxmemory:关注趋势线而不是单点数值。\n- 缓存命中率(如果你在做缓存):命中率低通常意味着键设计或 TTL 有问题,或读取被绕开。\n- 延迟:跟踪 p95/p99 命令延迟;峰值比平均值更重要。\n- 驱逐(evictions):持续驱逐通常意味着资源不足或 TTL 配置错误。\n- 复制延迟(若使用副本):上升的延迟会破坏读扩展与故障切换的信心。
快速排障:slowlog 与命令统计
当某些事情“变慢”时,用 Redis 自带工具确认:
- SLOWLOG 帮助识别昂贵命令(通常是大范围查询、大值获取或误用的全量扫描)。\n- 命令统计(通过 INFO)显示哪些命令占比最高。
KEYS、SMEMBERS或大范围的LRANGE突增常是红旗。
如果延迟上升但 CPU 看起来正常,也要检查网络饱和、超大负载或被阻塞的客户端。
容量规划与余量
通过保留余量(常见做法是 20–30% 的空闲内存)来为增长做规划,并在发布或功能开关后重新评估假设。把“持续驱逐”视为一次故障而非警告。
简单的事故运行手册
发生事故时按顺序检查:内存/驱逐、延迟、客户端连接、slowlog、复制延迟与最近部署。把常见原因记录下来并永久修复——仅靠告警不足以解决根本问题。
如果团队快速迭代,把这些运维预期内置到开发流程很有帮助。例如,用 Koder.ai 的规划模式与快照/回滚,你可以在加入 Redis 功能(如缓存或速率限制)时做压测、回滚并把实现保存在代码库中以便导出。
常见问题
在现代应用架构中,Redis 实际上用来做什么?
Redis 最适合作为共享的内存“快速层”,用于:
- 缓存昂贵的读取(API 响应、查询结果)
- 共享短暂状态(会话、锁、去重键)
- 高频计数(速率限制、浏览量)
- 轻量级工作分发(队列/流)
将主数据库用于持久且权威的数据和复杂查询。把 Redis 当作加速器和协调器,而不是你的记录系统。
Redis 能替代主数据库吗?
不是。Redis 虽然能持久化,但它并非“默认可靠”的持久存储。如果你需要复杂查询、强一致性或用于分析/报表的长期存储,还是把这些数据放在主数据库中。
如果丢失几秒钟的数据不可接受,不要假设通过默认设置能满足要求;要么仔细配置持久化策略,要么为该工作负载选择其它系统。
我该如何在 RDB、AOF 或两者之间选择持久化方案?
基于可接受的数据丢失和重启行为来决定:
- RDB 快照:重启加载快,但可能丢失自上次快照以来的写入。\n- AOF:按写操作追加日志,通常能减少数据丢失,但会增加开销并延长重放时间。\n- 两者兼用:常见折衷——用快照加速恢复,用 AOF 改善写入持久性。\n 先写下你的 RPO/RTO 目标,然后据此调整持久化策略。
什么是 cache-aside 模式,什么时候该使用它?
在 cache-aside 模式中,应用程序负责逻辑:
- 从 Redis 读。\n2. 命中则直接返回。\n3. 未命中则从数据库/API 获取。\n4. 将结果以 TTL 存入 Redis。\n5. 返回响应。\n 当应用能容忍偶发未命中且有明确的过期/失效策略时,这个模式很有效。
如何选择 TTL 以避免向用户返回令人惊讶的陈旧数据?
根据用户影响和后端负载来选 TTL:
- 根据数据的自然刷新频率选取(例如价格通常比头像短)。\n- 当缓存结构可能变更时,优先使用版本化键(例如
user:v3:123)。\n- 明确哪些场景允许一定程度的陈旧(例如信息流),哪些不允许(例如认证、库存)。\n 不确定时从较短的 TTL 开始,监测数据库负载后再调整。
热键过期时如何防止缓存雪崩(cache stampede)?
可采用以下一种或多种策略:
- 请求合并:只有一个请求负责重建值;其他请求等待或返回旧值。\n- TTL 抖动:对过期时间加随机偏移,避免大量键同时过期。\n- 软 TTL:短暂把值视为“可用但已陈旧”,由后台异步刷新。\n 这些模式能防止同步失效导致的数据库雪崩式压力。
在 Redis 中安全地存储会话有什么推荐做法?
常见做法:
- 将会话数据存到
sess:{sessionId},并设置与会话生命周期一致的 TTL。\n- 可选保存user:sessions:{userId}(Set)来支持“注销所有设备”。\n- 存储最小必要信息(ID、时间戳),不要把个人敏感数据放在 Redis 中。\n 避免在每次请求都延长 TTL(无限滑动过期),除非你有严格的控制策略,例如只在接近过期时延长。
如何用 Redis 正确实现速率限制?
用原子操作,避免计数器卡住或竞态:
- 对于固定窗口,不要把
INCR和EXPIRE作为分离的、未保护的调用执行。\n- 更推荐用 Lua 脚本 在创建计数器时同时递增并设置过期。\n 合理划分键的作用域(按用户、按 IP、按路由),并提前决定在 Redis 不可用时是 fail-open(放行)还是 fail-closed(拒绝),尤其是登录等敏感端点。
我应该为后台任务使用 Lists、Sorted Sets 还是 Streams?
根据持久性和运行需求选择:
- Lists(
LPUSH/BRPOP):最简单,但需要自己实现重试、可见性超时和在途任务跟踪。\n- Sorted sets:适合延迟任务或优先级调度(使用分数表示时间戳或优先级)。\n- Streams:通常是更好的默认选择——支持 consumer groups、应答(ACK)、消息挂起和崩溃恢复。\n 保持任务负载小,必要时把大对象放外部存储,仅传引用。
何时使用 Redis Pub/Sub,何时使用 Redis Streams?
当你需要快速广播且可以接受丢消息时,用 Pub/Sub(例如在线通知、实时 UI 更新)。其特点:
- 无持久化\n- 无确认机制\n- 无重放能力
如果每条事件都必须被可靠处理,优先选择 Redis Streams(持久化、consumer groups、重试与背压)。为运维规范化,也应通过 ACL 和网络隔离保护 Redis,并监控延迟与驱逐,保持类似 /blog/monitoring-troubleshooting-redis 的演练手册。