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

Redis 客户端缓存为什么还需要数据变更通知

来源:17golang原创

时间:2026-10-20 23:25:42 367浏览 收藏

Redis 客户端缓存并不是“把 GET 结果放进本地内存”这么简单。只要 Redis 中的同一个 key 还能被其他客户端修改,本地副本就可能继续返回旧值。数据变更通知的作用,就是在服务端发现相关 key 已经失效时,告诉客户端删除本地副本;下一次读取再回到 Redis 取得新值。

官方地址:https://redis.io/docs/latest/

下面只讨论 Redis 的 server-assisted client-side caching:为什么需要通知、默认 tracking 与广播模式有什么差异,以及连接断开时应该怎样处理。

先看本地缓存会怎样变旧

假设应用第一次读取 user:42,客户端把返回值放进本地缓存。之后另一个写入客户端执行 SET user:42 ...,原来的客户端并不会因为 Redis 的数据变了,就自动修改自己进程中的 map。没有通知时,它只能继续相信旧值,直到本地 TTL 到期或主动清理。

TTL 只能回答“最多允许旧多久”,不能回答“数据刚刚是否已经变更”。如果用户资料、权限配置或商品状态在短时间内被修改,固定 TTL 会在一致性和命中率之间被迫取一个保守值:TTL 太长,旧数据停留太久;TTL 太短,本地缓存收益又变小。

Redis 客户端缓存本地副本与服务端数据变更通知的静态关系图
图1:本地缓存副本、Redis 数据和失效通知之间的责任边界说明图;这是静态说明图,不是运行截图。

理解 Redis tracking 的通知关系

Redis 的 tracking 会记住某个连接读取过哪些 key。当其他客户端修改被跟踪的 key 时,Redis 向相关客户端发送 invalidation 消息,客户端收到后删除对应本地缓存。这个机制的关键不是把新值推送给所有客户端,而是把“旧值不能再用”的事实及时传达出去。

因此,客户端缓存通常按下面的职责分工工作:

  • 客户端决定哪些返回结果放进本地缓存,以及本地缓存的容量和淘汰策略。
  • Redis 服务端记录读取关系,并在 key 被修改时发送失效通知。
  • 客户端消费通知并删除旧副本;下一次 miss 再从 Redis 读取。

默认 tracking 还会关注过期和内存淘汰带来的失效。它并不保证客户端永远拿到“推送的新值”,而是通过删除旧值把下一次读取重新导向服务端。

用命令看清 tracking 的边界

下面的命令只用于展示协议关系。实际项目应由客户端库维护连接和本地缓存;不要把每次业务写入都手工复制成一条无关的广播消息。

# 连接进入服务端辅助的客户端缓存跟踪模式
redis-cli --resp3

# 开启默认 tracking:Redis 记录此连接读取过的 key
CLIENT TRACKING ON

# 读取结果可以由客户端放入自己的本地缓存
GET user:42

# 另一个客户端修改同一个 key 后,跟踪连接会收到失效消息
SET user:42 Flora

# 客户端收到通知后删除 user:42,下次读取再回源 Redis

这段关系也说明了一个容易忽略的边界:tracking 不是数据库事务,也不是变更日志。它负责通知“哪些已读 key 不能继续信任”,本地缓存仍然要负责并发安全、容量限制和消息消费。

区分默认 tracking 与广播模式

默认模式的通知范围比较精确:Redis 只向曾经读取过相关 key 的客户端发消息。它需要在服务端维护读取关系,但无效消息较少,适合 key 访问关系清晰、读多写少的场景。

广播模式使用 BCAST 和一个或多个 PREFIX。服务端不再记录每个客户端具体读过哪些 key,而是对匹配前缀的变更发送通知。这样可以减少服务端维护逐 key 关系的负担,但订阅者可能收到自己没有缓存过的 key 的无效消息,客户端过滤和消息流量成本会增加。

# 广播模式按 key 前缀接收通知,不表示客户端读过每个 key
CLIENT TRACKING ON BCAST PREFIX user:

# 只有匹配 user: 前缀的变更进入这类通知范围
SET user:42 Flora

# 取消当前连接的 tracking,重新配置时再选择另一种模式
CLIENT TRACKING OFF

可以用一个简单的判断来选模式:如果本地缓存清楚知道自己读过哪些 key,优先考虑默认 tracking;如果客户端需要按业务命名空间接收一整组 key 的变更,才考虑前缀广播。广播不是默认模式的“更强版本”,而是用更多通知换取更少的逐 key 记录。

Redis CLIENT TRACKING 默认模式、BCAST 广播模式与连接边界关系图
图2:Redis tracking 两种通知模式与连接边界的关系说明图;这是静态说明图,不是运行证据。

处理断线、连接池和竞态

失效通知依赖连接存活。Redis 官方文档明确提醒:如果客户端的任意连接断开,本地客户端缓存应清空,因为断开期间可能漏掉失效消息。连接恢复后再通过后续读取重新建立缓存关系。

RESP3 可以在同一连接上传输数据回复和失效 push 消息;某些实现也会使用两条连接,一条取数据,另一条接收失效通知。双连接模型要特别防范这样的竞态:数据连接发出 GET 后,失效连接先收到 key 已修改的消息,数据连接随后才返回旧版本。客户端应先把 key 标为“正在填充”,如果失效消息先到,就不要再用后到的旧回复覆盖清理结果。

在连接池场景中,还要确认客户端库是否把多个连接的 tracking 与本地缓存合并管理。不要只因为服务端接受了 CLIENT TRACKING ON,就假设库已经替你完成了通知消费、断线清理和并发保护。

用命中收益判断是否值得通知

客户端缓存最适合“读取频繁、修改相对少”的数据。热点计数器、实时排行榜或持续变化的状态可能产生大量失效消息,通知成本甚至抵消本地命中的收益。Redis 官方也建议把不适合缓存的数据放在不启用客户端缓存的独立连接上。

可以从下面几个维度做工程判断:

观察对象适合客户端缓存的信号需要谨慎的信号
读取频率同一批 key 被反复读取很少读取,缓存很快被淘汰
写入频率修改间隔较长每次请求都会写入
通知成本精确 tracking,失效消息可控宽前缀 BCAST,消息覆盖面很大
连接稳定性能监测断线并清空本地副本失效连接状态不可观测

常见问题

只设置本地 TTL,还需要 Redis 通知吗?

如果业务可以接受 TTL 内的旧值,可以只使用 TTL;如果修改后必须尽快停止返回旧值,TTL 不能替代失效通知。两者通常一起使用:通知负责快速删除,TTL 负责连接异常或消息丢失时的兜底。

默认 tracking 会通知所有客户端吗?

不会。默认模式按客户端连接读取过的 key 建立关系,只通知可能持有该 key 副本的客户端;按前缀扩大范围的是 BCAST 模式。

为什么连接断开后要清空整个本地缓存?

因为断开期间无法确定哪些 key 发生过变更。清空缓存是恢复一致性的保守动作,后续读取会重新建立可信副本。

总结一下:客户端缓存解决的是重复读取的网络和服务端开销,数据变更通知解决的是本地副本何时失去可信度。Redis tracking 让这两个问题能配合工作,但模式选择、连接生命周期和高频写入代价仍然需要由应用自己判断。

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