追求性能極致:客戶端緩存帶來的革命


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 總結

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 的變動(刪除、新增)。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM