2

追求性能极致:客户端缓存带来的革命 - Hello-Brand

 1 year ago
source link: https://www.cnblogs.com/wzh2010/p/15886809.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Redis系列1:深刻理解高性能Redis的本质
Redis系列2:数据持久化提高可用性
Redis系列3:高可用之主从架构
Redis系列4:高可用之Sentinel(哨兵模式)
Redis系列5:深入分析Cluster 集群模式
追求性能极致:Redis6.0的多线程模型

前面一篇我们说到,2020年5月份,Redis官方推出了令人瞩目的 Redis 6.0,提出很多新特性,包括了客户端缓存 (Client side caching)、ACL、Threaded I/O 和 Redis Cluster Proxy 等诸多新特性。如下:

image

我们也专门对 Redis 6.0的 Threaded I/O(多线程网络I/O 模式)做了很详细的说明,有兴趣的翻到前面一篇。
这一篇咱们就来聊下这个Client side caching(客户端缓存),看看Redis为什么需要客户端缓存、是基于什么原理实现的,以及具体应该怎么使用。

1 为什么需要客户端缓存

1.1 缓存服务的目的

回顾一下我们 在第一篇 《深刻理解高性能Redis的本质》中说过的,Redis的读写操作都是在内存中实现了,相对其他的持久化存储(如MySQL、File等,数据持久化在磁盘上),性能会高很多。因为我们在操作数据的时候,需要通过 IO 操作先将数据读取到内存里,增加工作成本。

image

上面那张图来源于网络,可以看看他的金字塔模型,越往上执行效率越高,价格也就越贵。下面给出每一层的执行耗时对比:

  • 寄存器:0.3 ns
  • L1高速缓存:0.9 ns
  • L2高速缓存:2.8 ns
  • L3高速缓存:12.9 ns
  • 主存:120 ns
  • 本地二级存储(SSD):50~150 us
  • 远程二级存储:30 ms

    我们举个L1和SSD的直观对比,如果L1耗时1s的话,SSD中差不多要15~45小时,所以内存层面的访问效率远远比磁盘层面的访问效率高很多。
    总之,缓存的目的是基于对持久化在磁盘的数据(比如MySQL数据、文件数据等)的高效访问,为了提升效率而实现的。《Redis in Action》中也提到, Redis 能够提升普通关系型数据库的 10 ~ 100 倍的性能。
    数据访问过程如下图,Redis 存储了热点数据,当天我们请求一个数据时,先去访问缓存层,如果不存在再去访问数据库,这样可以解决大部分高效读取数据的业务场景,性能是缓存最重要的价值之一。
    image

1.2 存在的问题

虽然我们使用Redis提升了数据的访问效率,但是依然存在一些问题。基于分布式访问的缓存服务是一个独立的服务存在,一般情况下访问它需要经过这几个步骤:

  • 连接缓存服务(一般不会跟计算服务在一个实例上)
  • 查找并读取数据(I/O操作)
  • 数据序列化反序列化
    这些操作一样的是对性能有影响的,随着互联网的发展,流量不断的膨胀,很容易达到 Redis 的性能上限。
    所以,我们经常会使用进程缓存(本地缓存),来辅助处理,将一些高频读低频写的数据暂存在本地,读取数据的时候,先检查本地缓存是否存在,不存在再访问远端缓存服务的数据,进一步提高访问效率。
    如果Redis也不存在,就只能去 数据库 中查询,查到的数据再设置到 Redis 和 本地缓存中,这样后续的请求就不用再走到数据库中了。
    image
    一般我们会使用Memcachced、Guava Cache 等来做第一级别缓存(本地缓存),使用Redis作为第二级缓存(缓存服务),本地内存避免了 连接、查询、网络传输、序列化等操作,性能比缓存服务快很多,这种模式大大减少数据延迟。

2 客户端缓存实现原理

Redis自己实现了一个客户端缓存,用以协助服务端Redis的操作,叫做tracking
我们可以通过命令来配置它:

CLIENT TRACKING ON|OFF [REDIRECT client-id] [PREFIX prefix] [BCAST] [OPTIN] [OPTOUT] [NOLOOP]

客户端缓存最核心的问题就是当Redis中的缓存变更或者失效了之后,如果能够及时有效的通知到客户端缓存,来保证数据的一致性。
Redis 6.0 实现 Tracking 功能,这个功能提供了两种方案来实现数据的一致性保证:

  • RESP2 协议版本的转发模式
  • RESP3 协议版本的普通模式和广播模式
    image
    接下来我们一个个来分析。

2.1 普通模式

Redis使用 TrackingTable 来存储普通模式的客户端数据,它的数据类型是基数树 ( radix tree)。
radix tree是针对稀疏的长整型数据查找的多叉搜索树,能快速且节省空间的完映射,想深入了解的可以看这篇介绍

image

如图中,客户端ID列表与Redis存储键的指针具有映射关系。而Redis键对象的指针对应的就是内存地址,数据结构是Long。
当开启了track 功能之后,操作具有以下特性:

  • 当Redis获取一个键值信息时,radix tree 会调用 enableTracking 方法记录 key 和 clientId 的映射关系,记录到 TrackingTable 中。
  • 当Redis删除或者修改一个键值信息时
    • radix tree 根据key调用 trackingInvalidateKey 方法查找对应的 Clinet ID
    • 调用 sendTrackingMessage 方法把失效的键值信息(invalidate 消息) 发送给这些 Clinet ID。
    • 发送完成之后从TrackingTable中删除映射关系。
  • Client关闭 track 功能后,遇到大量删除操的时候,一般是懒删除,只将 CLIENT_TRACKING 标志位删除。
  • 默认 track 模式是不开启,需要通过命令开启,参考如下:
CLIENT TRACKING ON|OFF
+OK
GET test
$7
archite

2.2 广播模式(BCAST)

image

广播模式与普通模式类似,也是采用映射关系来对照,但实现过程还是有区别的:

  • 存储的内容不一样:如图,采用Prefix Table 来存储客户端数据,存储的是 前缀字符串指针 和 客户端数据(客户端ID列表 + 需通知的key值列表) 的映射关系。
  • 删除键值的时机不一样:
    • radix tree 根据key调用 trackingInvalidateKey 方法查找PrefixTable。
    • 判断是否为空,不为空则 调用 trackingRememberKeyToBroadcast 对键列表进行进行遍历,找到符合前缀匹配规则的,并记录位置。
    • 在事件处理周期函数 beforeSleep 中 调用 trackingBroadcastInvalidationMessages 函数来发送消息。
    • 发送完成之后从 PrefixTable 中删除映射关系。

2.3 转发模式

RESP 3 协议 是 Redis 6.0 新启用的协议,使用普通模式或者广播模式需要依赖这种协议,这样对于RESP 2 协议的客户端来说就会有问题。所以衍生除了另一种模式:重定向(redirect)。

  • RESP 2 无法直接 PUSH 失效消息,所以不能直接获取到失效数据(Redis Client 2)。
  • 支持 RESP 3 协议的客户端(Redis Clinet 1) 告诉 Server 将失效消息通过 Pus/Sub 通知给 RESP 2 客户端。
  • 而Redis Client 2 (RESP 2 )是通过订阅命令 SUBSCRIBE,专门订阅用于发送失效消息的频道 redis:invalidate。
    image

如下所示:

# Redis Client 2 (支持RESP 2)执行订阅 
client id : 888
subscribe _redis_:invalidate

# Redis Client 1(支持RESP 3),转发给 2
client tracking on bcast redirect 888

3.1 默认模式(普通模式)

  • 服务端记录客户端操作过的 key,key 对应的值发生变化时,会发送 Invalidation Messages 给Redis 客户端。
  • 服务端记录key信息会消耗一些内存,但是发送失效消息的范围,限制在存储的key范围内,计算和网络传输变的轻量。
  • 优点是节省 CPU 以及流量带宽,但是会占用一些内存。

3.2 广播模式

  • 服务端不记录 key,而是订阅 key 的特定前缀,当匹配前缀的 key 的值改变时,发送 Invalidation Messages 给 Redis客户端。
  • 优点是服务端的内存消耗少,但是会损耗更多的 CPU 去做前缀匹配的计算。

3.3 转发模式

  • 为了兼容 resp2 协议的一种过渡模式
  • 优点是占用内存少,CPU占用多

客户端的缓存

客户端缓存,需要业务侧自己实现,Redis 服务端只负责通知你key 的变动(删除、新增)。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK