作者:Wen Hui
转载:中间件小哥
客户端追踪是Redis 6中引入的新概念。这个特性主要辅助客户端在Redis服务端键值被其他客户端更新后,能及时通知客户端将缓存过的键值逐出并更新。从而减少或避免数据一致性带来的问题。目前的客户端追踪包含以下模式:
1)普通追踪模式
命令:CLIENT TRACKING ON
特点:
1.当客户端开启追踪时,服务器端保存一个无效表(Invalidation Table)来记录所有相应客户端读取过的键的信息。
2.当相应的键被更改时,向相应的客户端发送缓存无效信息。
普通追踪模式优点:
只针对特定客户端发送键无效信息。节省服务器端和客户端CPU资源。
追踪模式缺点:
相对于其他模式来说耗费更多服务器端内存,因为需要记住启用普通追踪模式的客户端访问过的所有键。
例子:
如下图所示,左上窗口代表客户端1连接,左下代表客户端1的缓存失效消息连接,右边窗口代表客户端2,在客户端1查询并模拟缓存过foo键的值后,如果客户端2随后更新foo的值,则客户端1的缓存失效消息连接会接收到服务器的消息,通知foo键已经被更新过了,需要客户端重新查询并缓存新的值。
2)广播追踪模式
命令:CLIENT TRACKING ON BROADCAST PREFIX
特点:

  1. 服务器保存一个键前缀表(Prefix Table)来记录客户端需要追踪的键前缀,而不是记录所有相应的客户端访问过的键。
  2. 当满足特定键前缀中的任何键被更新时,服务器向所有订阅特定前缀的客户端发送缓存无效信息。
    广播追踪模式优点:
    服务器端只需要记住客户端感兴趣的键前缀信息,节省服务器端内存。
    广播追踪模式缺点:
    如果特定键前缀中的任何键被更新时,服务器需要向所有订阅该键前缀的客户端发送缓存无效消息。耗费服务器端和客户端CPU及网络资源,并且客户端可能收到很多没有缓存过的键的无效消息。
    例子:
    如下图所示,左边1列代表客户端1的客户端连接和缓存失效消息连接,中间一列代表客户端2的客户端连接和缓存失效连接,右边一个窗口代表客户端3.首先客户端1和客户端2开启了广播追踪模式,客户端1注册了foo:和bar:两个键前缀,客户端2注册了foo:和ttt:两个键前缀。客户端1和2分别模拟查询并缓存了foo:1和foo:2两个键。在客户端3更新foo:1后,因为客户端1和2都注册了foo:前缀,所以都会收到缓存失效的消息,即使客户端2没有缓存foo:1键的值。

Redis 6客户端追踪简介_第1张图片

3)普通追踪模式下的OPT IN模式
命令:CLIENT TRACKING ON OPTIN
特点:
客户端需要显式通过CLIENT CACHING YES命令指定下一个读请求的键需要被追踪(默认情况下不追踪),其他与普通追踪模式相同。
例子:
如下图所示,左边两个窗口代表客户端1的客户端连接和缓存失效消息连接,右边窗口代表客户端2.客户端1启用客户端追踪OPT IN模式并模拟缓存了foo键,当客户端2更新foo键的值后客户端1没有接收到缓存失效的消息,因为OPT IN模式是默认不开启的。当客户端显式使用CLIENT CACHING YES 并缓存foo键的值后,客户端2更新foo键的值,这时客户端1会接收到缓存失效消息。但CLIENT CACHING YES只对下一个读请求有效。

Redis 6客户端追踪简介_第2张图片

4)普通追踪模式下的OPT OUT模式
命令: CLIENT TRACKING ON OPTOUT
特点:
用户需要显式通过CLIENT CACHING NO指定下一个键不需要追踪(默认情况下追踪),其他与普通追踪模式相同。
例子:
如下图所示,和上一个例子类似,左边两个窗口代表客户端1的客户端连接和缓存失效消息连接,右边窗口代表客户端2.客户端1启用客户端追踪OPT OUT模式并模拟缓存了foo键,与OPT IN模式相反,当客户端2更新foo键的值后客户端1会收到缓存失效消息。但当客户端1使用CLIENT CACHING NO并缓存rrr键时,客户端2更新rrr键,客户端1没有接收到缓存失效消息。因为客户端已经显示指定这个键rrr不需要被追踪,与OPT IN 模式相同,CLIENT CACHING NO只对下一个读请求有效,当客户端1缓存ttt并被客户端2修改时,客户端1会恢复收到缓存失效的消息。

Redis 6客户端追踪简介_第3张图片

OPT IN/OUT模式优点:
相对于普通追踪模式而言,OPT IN/OUT 模式可以显式指定那些键需要被追踪哪些不需要,因此相对普通追踪模式来说更节省服务器端内存和CPU处理时间。
OPT IN/OUT模式缺点:
程序需要更细粒度控制对每个键进行追踪。以此带来的实现复杂度。
目前Redis客户端缓存需要注意问题
目前大部分的Redis客户端目前没有支持RESP3 协议,如果使用RESP2协议需要创建额外的发布订阅客户端连接来接收缓存无效信息。

  1. Proxy端的支持。
  2. 双连接模式下,客户端更新本地缓存和接收其他客户端更改数据导致的Redis缓存无效消息间存在竞态条件(race condition)。需要在客户端程序中做仔细的处理。
    Future
    根据Salvatore的社交媒体消息,未来客户端追踪部分代码可能会进行相应改变,其中主要变动是在特定情况下,Redis服务端发送给客户端中的缓存无效消息中直接将更改过的键值放入其中,从而避免客户端再向Redis服务器端读取相应的更新过的键值。从而节省服务器端和客户端的CPU和节省系统网络带宽。
    在接下来的文章中,我们会讲解这一部分的Redis源码实现。
    参考资料:
    • https://redis.io/topics/client-side-caching
    • https://github.com/antirez/redis