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 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 官方文档明确提醒:如果客户端的任意连接断开,本地客户端缓存应清空,因为断开期间可能漏掉失效消息。连接恢复后再通过后续读取重新建立缓存关系。
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 让这两个问题能配合工作,但模式选择、连接生命周期和高频写入代价仍然需要由应用自己判断。
-
117 收藏
-
426 收藏
-
171 收藏
-
113 收藏
-
195 收藏
-
135 收藏
-
105 收藏
-
138 收藏
-
282 收藏
-
333 收藏
-
186 收藏
-
373 收藏
-
129 收藏
-
489 收藏
-
200 收藏
-
360 收藏
-
435 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习