CLIENT TRACKING ON 本身不自动广播,必须满足三个硬性条件:使用 RESP3 协议、显式读取触发 key 登记、服务端配置匹配的 BROADCAST 或 REDIRECT 模式,否则无法收到 invalidate 消息。

能减少网络请求,但必须满足三个硬性条件:客户端用 RESP3 协议、显式读取触发追踪、服务端配置匹配的广播/重定向模式;否则 CLIENT TRACKING ON 只是开了一扇没装门的窗。
为什么开了 CLIENT TRACKING ON 却收不到失效消息
根本原因不是命令没执行,而是默认的 REDIRECT 模式没指定目标 client id,且服务端压根不主动广播——它只在你“读过某个 key”之后,又“有其他客户端改了这个 key”,才推一条 invalidate 消息。常见卡点:
- 客户端仍用 RESP2(比如老版本
redis-cli),HELLO 3没发成功,push 消息直接被忽略 - 没真正执行过
GET/HGET等读命令,tracking table 里没记录该 client 和 key 的关联 - 服务端没配
notify-keyspace-events "Ex"或"Ee",但注意:Ee在 LRU 驱逐时并不触发 tracking 广播(这是个高频误解) - 用了
BROADCAST PREFIX user:,但写操作用的是SET user_10086(下划线 ≠ 冒号),前缀不匹配
CLIENT TRACKING ON 后,哪些操作会进 tracking table
只有满足「显式读 + RESP3 连接 + 未超 tracking-table-max-keys」的 key 才会被登记。具体行为:
-
GET user:10086→ 记入 tracking table(client A 关心这个 key) -
MGET user:10086 user:10087→ 两个 key 都记入 -
GETRANGE user:10086 0 10→ 记入(属于读操作) -
EXISTS user:10086→ 记入(Redis 6.2+ 支持,6.0 默认不记) -
SCAN/KEYS/TYPE→ 不记(非精确读,不触发 tracking) - 管道中多个
GET→ 每个 key 单独判断是否符合登记条件
一旦超出 tracking-table-max-keys(默认 100 万),Redis 用 LRU 淘汰旧条目,不会报错也不会警告。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
广播模式下怎么避免收到一堆无用的 invalidate
BROADCAST 看似省事,实际容易让客户端被无效消息淹没。关键控制点在 prefix 设计和客户端过滤逻辑:
- prefix 必须严格匹配 key 命名规范,比如约定所有用户数据以
user:开头,就别混用user_或users: - 一个 client 可注册多个 prefix:
CLIENT TRACKING ON BROADCAST PREFIX user: order: product: - 服务端不校验 prefix 是否真有对应 key,只做字符串前缀匹配;所以
PREFIX a:会匹配a:b、a::c、甚至a:(空后缀) - 客户端收到
invalidate ["user:10086"]后,应先查本地缓存是否存在该 key,再决定是否清理——避免“收到即删”导致缓存击穿 - 慎用
NOLOOP:它防止自己改 key 后又收到自己的推送,但前提是 client 自己发写命令时带上CLIENT REPLY OFF配合,否则无效
本地缓存更新后,如何避免重复拉取或漏通知
最常被忽略的环节是客户端对 push 消息的消费时机。Lettuce、Jedis 等驱动处理方式差异大:
- Lettuce(推荐):需启用
StatefulRedisConnection并注册PushHandler,invalidate消息在 IO 线程异步到达,不能在 handler 里直接阻塞调用 Redis - Jedis:原生不支持 RESP3 push,必须用 Jedis 4.x + 手动轮询
CLIENT GETREDIR或退化为 Pub/Sub 模拟 - 收到
invalidate ["key1", "key2"]后,不要立刻发GET—— 应走异步加载队列,防瞬间并发打垮 Redis - 如果本地缓存用 Caffeine,建议设置
expireAfterWrite作为兜底(比如 5 分钟),避免 tracking 中断时彻底失联
真正难的不是开启 tracking,而是让每个 client 的读、写、失效、重载路径全部对齐语义——稍有错位,就会出现“本地有旧值但没人通知删”或“刚删完又被新值覆盖前重复拉取”这类毛刺问题。

















