登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  数据库 >  Redis

Redis 客户端缓存如何用广播模式减少失效消息

来源:17golang原创

时间:2026-10-08 22:51:53 484浏览 收藏

Redis 的 BCAST 广播模式不会天然减少失效消息:它用“按前缀广播”替代服务端逐键记录,通常会让客户端收到更多通知。真正能减少无关消息的是把键空间设计成稳定、互不重叠的业务前缀,并让每类客户端只订阅自己确实缓存的前缀,绝不能省略 PREFIX 或使用过宽的公共前缀。

如果一个商品服务只缓存 product:,就不要让它同时接收 user:、order: 的失效通知。这个迁移的目标不是追求“零消息”,而是让收到的每条失效通知都尽量与本地缓存有关,同时把 Redis 服务端的逐键跟踪内存换成一个更小、更稳定的前缀表。

Redis 官方参考:https://redis.io/docs/latest/develop/reference/client-side-caching/

先确认这次迁移解决什么问题

Redis 客户端缓存有两种跟踪方式。普通模式会记住某个客户端读过哪些键,键被修改、过期或因内存策略被淘汰时,只通知可能缓存了该键的客户端。它的消息比较精准,但服务端要维护失效表,内存开销与被跟踪的键和客户端有关。

BCAST 模式不保存逐键访问记录,而是保存“前缀对应哪些客户端”。只要某个键匹配已注册前缀,订阅该前缀的所有客户端都会收到失效消息。它降低了服务端跟踪内存,却把筛选责任交给前缀设计和客户端。

Redis 普通客户端跟踪与 BCAST 广播模式的静态结构对照图
图1:普通跟踪与 BCAST 的静态结构对照。广播模式减少的是服务端逐键记录,不是天然减少客户端通知。
对比项普通跟踪模式BCAST 广播模式
服务端记录键与可能缓存它的客户端前缀与订阅它的客户端
失效消息更接近实际读取过的键前缀内所有被修改的键
主要成本失效表内存与维护成本客户端消息量和前缀匹配 CPU
适合场景键分散、客户端读取集合差异大键空间可按少量稳定业务域划分

旧配置为什么会制造大量无关通知

最危险的旧写法是只启用 BCAST,却没有配置任何 PREFIX。Redis 会把前缀视为空字符串,也就是所有被修改的键都匹配。另一个常见问题是使用 app: 这类覆盖整个系统的前缀,导致只缓存商品的进程也接收用户、订单和库存的失效消息。

# 不推荐:没有 PREFIX 时等价于订阅整个键空间
redis-cli CLIENT TRACKING ON BCAST

# 不推荐:app: 覆盖范围过大,业务客户端会收到大量无关失效
redis-cli CLIENT TRACKING ON BCAST PREFIX app:

这类配置在功能上可能没有报错,但会增加网络流量、失效回调次数和本地缓存锁竞争。迁移前应先统计客户端真正缓存的键族,而不是从现有键名里随便截取一个共同前缀。

用不重叠的业务前缀收窄广播范围

把键设计为 user:42、product:9、order:20261008:1001 这类稳定业务域,随后按进程职责注册前缀。Redis 官方文档要求同一跟踪配置中的前缀不能覆盖重叠键空间,例如 foo 与 foob 不能同时使用,因为键 foobar 会同时匹配两者。

# 用户服务只接收 user: 键的失效消息
redis-cli CLIENT TRACKING ON BCAST PREFIX user:

# 商品服务只接收 product: 键的失效消息
redis-cli CLIENT TRACKING ON BCAST PREFIX product:

# 同一客户端确实缓存两个业务域时,可以注册多个不重叠前缀
redis-cli CLIENT TRACKING ON BCAST PREFIX user: PREFIX product:

配置后可用 CLIENT TRACKINGINFO 查看当前连接的跟踪状态和前缀集合。核对重点不是命令是否返回 OK,而是前缀是否只覆盖这个客户端的本地缓存域。

# 在启用跟踪的同一连接上检查 flags 与 prefixes
redis-cli CLIENT TRACKINGINFO
Redis 键前缀、失效连接、数据连接与本地缓存的静态关系图
图2:按业务域拆分 PREFIX 后的失效消息结构图。客户端只订阅自己缓存的键空间,并在失效连接丢失时清空本地缓存。

失效连接要和数据连接一起设计

RESP3 可以在同一连接上接收普通回复和 push 类型的失效消息。如果客户端库使用两条连接,数据连接可通过 REDIRECT 把消息送到专用失效连接;RESP2 也依赖这种模式,并通过特殊的 __redis__:invalidate 通道承载定向消息。这里不是普通意义上的全体 Pub/Sub 广播,只有指定的重定向连接会收到消息。

# 第一步:在专用失效连接上获取连接 ID,例如返回 42
redis-cli CLIENT ID

# RESP2 失效连接订阅 Redis 专用失效通道
redis-cli SUBSCRIBE __redis__:invalidate

# 第二步:数据连接把 BCAST 失效消息定向到连接 42
redis-cli CLIENT TRACKING ON REDIRECT 42 BCAST PREFIX product:

连接池场景可以让多条数据连接重定向到同一条失效连接,但应用必须把“失效连接是否健康”纳入缓存有效性。如果失效连接断开,继续读取本地缓存可能产生陈旧数据。官方建议在连接丢失或心跳无响应时清空本地缓存,再重建连接和跟踪配置。

旧实现迁移时要补上的三个边界

写后立刻收到自己的失效消息

默认情况下,修改键的客户端也会收到失效通知。如果应用在写入成功后已经把新值放进本地缓存,可以使用 NOLOOP 避免马上清掉自己刚写入的值。只有明确实现了写后更新缓存,才应开启这个选项。

# 写路径会同步更新本地缓存时,用 NOLOOP 抑制自身写入的失效回环
redis-cli CLIENT TRACKING ON BCAST NOLOOP PREFIX product:

双连接的读写竞态

数据回复和失效消息走不同连接时,失效消息可能先到,而较旧的数据回复后到。如果客户端直接把后到的回复写入缓存,就会重新放入陈旧值。更稳妥的做法是在发起读取时放一个“缓存中”占位;若期间收到失效消息就删除占位,数据回复回来时发现占位已不存在,便不再写入本地缓存。

// 发出 GET 前先占位,用于识别读取期间到达的失效消息
localCache.set(key, { state: "loading" });

const value = await redis.get(key);

// 只有占位仍存在才写入;失效回调删除占位后,这里会跳过旧值
if (localCache.get(key)?.state === "loading") {
  localCache.set(key, { state: "ready", value });
}

TTL 和客户端内存没有上限

广播只解决失效通知,不替代本地淘汰策略。客户端仍应限制内存,并为缓存项设置最大 TTL;即使 Redis 键没有 TTL,本地最大存活时间也能降低连接故障或实现缺陷导致长期陈旧数据的风险。频繁变化或很少访问的键通常不值得进入客户端缓存。

回归检查不要只看 Redis 内存

迁移完成后,至少比较三个指标:Redis 跟踪相关内存是否下降、客户端收到的失效消息中有多少真正命中本地缓存、业务请求的本地缓存命中率是否保持稳定。BCAST 前缀数量过多时,Redis 的 CPU 成本会随注册前缀数量增长,因此前缀不是越细越好。

检查项理想变化异常时怎么处理
服务端逐键跟踪内存BCAST 后不再维护普通失效表中的逐键记录确认连接确实启用了 BCAST
无关失效消息比例细分 PREFIX 后明显下降继续拆分过宽前缀或调整服务职责
本地缓存命中率不因过度失效而明显下跌检查高频写键是否不适合缓存
Redis CPU保持在容量阈值内减少前缀数量,避免过细划分
失效连接健康断线时清空缓存并成功重连优先停用本地缓存,不能继续读旧值

可执行的迁移清单

  1. 列出每类应用实际缓存的 Redis 键族,剔除高频变化和低频读取的键。
  2. 把键名整理为少量稳定、不重叠的业务前缀,不使用空前缀和全局公共前缀。
  3. 先在单个实例启用 CLIENT TRACKING ON BCAST PREFIX ...,核对 TRACKINGINFO。
  4. 确认失效回调只删除本地对应键,并实现连接断开后清空本地缓存。
  5. 双连接实现加入读取占位,避免失效消息与数据回复乱序造成陈旧值回填。
  6. 只有写后会更新本地缓存的连接才开启 NOLOOP。
  7. 灰度比较消息命中率、缓存命中率、Redis CPU 与网络流量,再逐步扩大范围。
  8. 若无关通知仍然过多,回退到普通跟踪模式,或重新划分键前缀,而不是无限增加前缀。

相关问题

BCAST 一定比普通跟踪模式省资源吗?

不一定。它节省逐键跟踪内存,却可能增加客户端消息、网络流量和前缀匹配 CPU。是否合适取决于键空间能否被少量稳定前缀划分。

为什么不能同时使用 user: 和 user:profile:?

两者覆盖重叠键空间,某些键会同时匹配。应保留能覆盖需求的一个前缀,或改成互不重叠的命名。

失效连接断开后可以继续使用本地缓存吗?

不建议。客户端可能已经漏掉修改通知,应先清空本地缓存并恢复失效连接,再重新积累缓存。

什么时候应该放弃 BCAST?

当客户端读取集合高度离散、键前缀无法稳定划分,或无关失效消息与 CPU 成本高于普通跟踪模式时,普通逐键跟踪通常更合适。

广播模式的核心取舍很清楚:Redis 不再记住每个客户端读过哪些键,客户端则要接受前缀内的广播。把前缀设计成业务边界,并补齐断线清理、竞态保护和容量指标,才能真正减少无关失效消息,而不是把服务端内存压力转化成客户端消息风暴。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>