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

Redis CLIENT TRACKING 怎样让客户端感知缓存失效

来源:17golang原创

时间:2026-10-26 21:16:37 166浏览 收藏

如果应用把热点 Redis 数据复制到进程内缓存,真正麻烦的不是第一次读取,而是 Redis 中的值变化后,客户端怎样知道自己的副本已经不能用了。CLIENT TRACKING解决的是“通知”问题:Redis 记录客户端读过的 key,后续 key 被修改、过期或因内存策略被淘汰时,向相关客户端发送失效消息;本地缓存仍由应用自己维护。

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

最小可行方案是:客户端开启 tracking,读取 key 后把响应放入本地缓存;收到 INVALIDATE 或 RESP3 push 消息时删除对应 key。若使用连接池,优先考虑 RESP3 单连接,RESP2 则使用专用失效连接。

本文只讨论 Redis 的服务端辅助客户端缓存,不把它扩展成 Pub/Sub 广播缓存、分布式锁或完整缓存框架教程。

一、先明确 CLIENT TRACKING 负责什么

Redis 不会因为执行了 CLIENT TRACKING ON 就替应用保存一份本地数据。客户端仍要决定哪些响应进入进程内缓存、缓存多久以及收到通知后如何删除。Redis 负责维护“某个客户端可能读过哪些 key”的关系,并在 key 变化时发通知。

# 用 RESP3 打开交互会话,允许同一连接接收 push 消息
redis-cli -3

# 开启默认 tracking;此时连接仍然没有自动本地缓存
CLIENT TRACKING ON

# 这次只表示客户端读过 user:1001,应用可自行把返回值放入本地缓存
GET user:1001

默认模式会跟踪只读命令提到的 key。另一个客户端执行 SET user:1001 ...,或者 key 因过期、maxmemory 策略被淘汰时,Redis 会向跟踪客户端发送失效通知。客户端收到通知后必须删除本地副本,否则仍可能返回旧值。

Redis CLIENT TRACKING 中应用本地缓存、跟踪关系和失效通知的静态结构图
图1:结构说明图,查看应用本地缓存、Redis 跟踪表、写入 key 与失效通知之间的职责边界。

二、先选消息通道:RESP3 单连接还是 RESP2 双连接

RESP3 可以在同一条连接上交错接收普通命令响应和 push 消息,客户端实现相对直接。若客户端仍使用 RESP2,常见做法是准备一条专门接收失效消息的连接,再把数据连接的 tracking 重定向到它。

# 失效连接:先取得自己的 Redis client ID
CLIENT ID
# 假设返回 4;这个连接只负责接收失效通知
SUBSCRIBE __redis__:invalidate

# 数据连接:把 tracking 的通知重定向到 client ID 4
CLIENT TRACKING ON REDIRECT 4

# 读取后,应用把响应和 key 放进自己的本地缓存
GET user:1001

RESP2 的特殊频道名只是协议层的接收方式,不代表消息会像普通广播一样送给所有订阅者;Redis 会把失效消息定向到 REDIRECT 指定的连接。连接池场景要把多个数据连接和失效连接的生命周期绑定,不能只为某一次请求临时创建订阅连接。

Redis RESP3 单连接与 RESP2 双连接的失效通知结构对照图
图2:结构对照图,查看 RESP3 数据通道与失效 push、RESP2 数据连接与 REDIRECT 失效连接的静态关系。

三、按缓存范围选择四种 tracking 策略

默认 tracking 适合“读到的对象大多值得缓存”的场景;如果只有少量查询应该进入本地缓存,使用 OPTIN 可以减少无关跟踪:

# 只跟踪显式标记的下一次读取
CLIENT TRACKING ON OPTIN

# CLIENT CACHING YES 只作用于紧接着的读取命令
CLIENT CACHING YES
GET user:1001

OPTOUT 则相反:默认跟踪读到的 key,需要排除某个 key 时使用 CLIENT UNTRACKING。如果客户端不希望 Redis 为每个客户端保存 key 关系,而是按业务前缀接收通知,可以用 BCAST:

# 按 key 前缀接收失效通知,不建立普通模式的逐 key 跟踪表
CLIENT TRACKING ON BCAST PREFIX user: PREFIX object:

# 只要匹配前缀的 key 被修改,相关客户端就可能收到通知
SET user:1001 Flora

BCAST 省掉了按客户端和 key 维护的跟踪内存,但会把某个前缀下的变化通知给订阅该前缀的客户端,通知数量可能增加。前缀不能重叠,例如同时注册 foo 与 foob 会产生冲突。

四、把竞态和断连当成缓存一致性的一部分

双连接方案有一个容易忽略的竞态:数据连接发出 GET 后,失效连接可能先收到某个更新的通知,GET 的旧响应随后才到达。如果客户端在收到通知后简单地把后到的响应重新写入本地缓存,旧值就会复活。

稳妥做法是在发送 GET 时先放入“读取中”占位;收到失效消息时删除占位;如果响应到达时发现该 key 已被删除,就不要把这次响应写回缓存。使用同一条 RESP3 连接时,消息顺序更容易管理,但客户端仍要正确处理 push 消息。

失效连接断开时也不能继续相信本地缓存。应立即清空受影响的本地缓存,并通过周期性 PING 判断失效通道是否仍然存活;在重连并重新建立 tracking 前,宁可回源读取。

NOLOOP适合客户端希望把自己写入的值直接放进本地缓存的情况。它会抑制写入连接收到的自身失效通知,但默认 tracking 下该 key 仍会从该连接的跟踪关系中移除;要继续接收未来变化,客户端需要再次读取并建立跟踪。

五、上线前用这张速查表收口

问题建议必须记住的边界
客户端支持 RESP3优先单连接 tracking正确消费 push 消息,不能只处理普通响应
仍是 RESP2 或连接池限制数据连接 + REDIRECT 失效连接失效连接断开时清空本地缓存
只有少数读取需要缓存OPTIN + CLIENT CACHING YES标记只影响紧随其后的读取
按业务前缀共享失效BCAST + PREFIX通知更宽,前缀不能重叠
客户端本地缓存很大增加容量淘汰和最大 TTLtracking 不替你解决本地内存上限

最后再强调一次:CLIENT TRACKING 的结果不是“Redis 自动同步了客户端缓存”,而是“客户端得到足够精确的失效线索”。应用仍要负责缓存写入、删除、TTL、连接重连和竞态保护。把这几项职责分清,才能判断一条缓存失效通知是否真的让客户端停止提供旧数据。

常见问题

CLIENT TRACKING 会跟踪写命令吗?

默认跟踪关注客户端读取过的 key。写入会触发相关 key 的失效通知,而不是把写入结果自动复制到所有客户端的本地缓存。

为什么收到失效通知后还需要设置本地 TTL?

连接断开、客户端处理错误或边界竞态都可能让通知链路失效。一个合理的最大 TTL 是第二道保险,避免本地副本永久存活。

BCAST 是否一定比默认 tracking 更省资源?

它减少 Redis 维护逐客户端 key 关系的内存,但可能向更多客户端发送通知,并增加按前缀处理的 CPU 成本。应按 key 变化频率、前缀数量和客户端数量选择。

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