引入『客戶端快取』,Redis6算是把快取玩明白了…
原創:微信公眾號
碼農參上
,歡迎分享,轉載請保留出處。
哈嘍大家好啊,我是沒更新就是在家忙著帶娃的Hydra。
在前面介紹兩級快取的文章中,我們總共給出了4種實現方案,在項目中整合了本地快取Caffeine
和遠程快取Redis
,將應用的性能從僅適用單獨遠程快取的基礎上,再次提高了一個層次。
而今天Hydra要和大家分享的技術,在思想上和上面兩級快取有些類似,不過不需要藉助其他本地快取中間件,只使用Redis
自身服務端和客戶端就可以實現。這就是Redis6
中的客戶端快取Client-side caching
這一項新特性,它允許將數據快取在應用服務端以及遠程快取兩個位置。
簡介
客戶端快取是Redis6眾多新特性中比較實用的一項新功能,我們看看官方文檔,了解一下它的作用:
客戶端快取是一種用於創建高性能服務的技術,它可以利用應用伺服器上的可用記憶體(這些伺服器通常是一些不同於資料庫伺服器的節點),在這些應用服務端來直接存儲資料庫中的一些資訊。
與訪問資料庫等網路服務相比,訪問本地記憶體所需要的時間消耗要少得多,因此這個模式可以大大縮短應用程式獲取數據的延遲,同時也能減輕資料庫的負載壓力。
看到這,我心想這不是和其他本地快取Guava、Caffeine啥的一樣嗎,換湯不換藥,都是使用的應用服務的記憶體罷了。要說有什麼好處,可能就是我在項目中能少引入一個中間件了。
不過,我這點淺薄的猜想,在看完客戶端快取的具體應用模式後,徹底被顛覆了。
兩種模式
在了解了客戶端快取的基本功能後,我們來看看它的兩種基本應用模式。Redis的客戶端快取支援被稱為tracking
,個人感覺翻譯為對key的追蹤就很好理解,它具有兩種模式:
- 默認模式,服務端會記錄某個客戶端具體訪問過哪一些
key
,當這些key
對應的值發生變化時,會發送失效消息給這些客戶端。這個模式會在服務端消耗一些記憶體,但是發送失效消息的範圍,被限制在了客戶端存儲了的key
的集合範圍內 - 廣播模式,服務端不會再記錄某個客戶端訪問了哪些
key
,因此這個模式不消耗服務端的記憶體。取而代之的是,客戶端需要訂閱key
的特定前綴,每當符合這個前綴的key
對應的值發生改變時,客戶端都會收到通知消息
看到這裡,它和我們之前使用的兩級快取之間差異,是不是已經初露端倪了呢?如果還不熟悉兩級快取的架構,那麼可以先來看看下面的這張圖:
這種架構在理論上看起來不錯,但是實際使用起來需要注意的點不少,尤其是在分散式模式下,需要保證各個主機下的一級快取的一致性問題,回想一下我們原先的解決方案,可以使用redis本身的發布/訂閱功能來實現:
而客戶端快取的出現,大大簡化了這一過程。我們以默認模式為例,看一下使用了客戶端快取後的操作過程:
相比原先的發布/訂閱模式,我們可以看到明顯的優勢,使用客戶端快取功能後,我們只需要單純的修改redis中的數據就可以了,手動處理髮布/訂閱消息的這一過程可以完全被省略。
優勢
到這裡,在了解了客戶端快取的基本功能與兩種模式後,我們來對比一下,和傳統的只使用redis做遠程快取、以及使用整合後的兩級快取相比較,客戶端快取具有什麼樣的優勢。
- 當應用的服務端存在快取時,會直接讀取本地快取,能夠減少網路訪問上造成的延遲,從而加快訪問速度
- 同時也能減少訪問redis服務端的次數,降低redis的負載壓力
- 在分散式環境下,不再需要通過發布訂閱來通知其他主機更新本地快取,來保證數據的一致性。使用客戶端快取後,它所具有的原生的消息通知功能,能很好地支援作廢本地快取,保證之後訪問時能取到更新後的新數據
誤區
在開始演示客戶端快取的使用之前,我們先來糾正一個誤區。
雖然這個新特性被稱為客戶端快取,但是redis本身不提供在應用服務端快取數據的功能,這個功能要由訪問redis的客戶端自己去實現。
說白了,也就是redis服務端只負責通知你,你快取在應用服務本地的這個key已經作廢了,至於你本地如何快取的這些數據,redis並不關心,也不負責。
功能演示
下面將通過一些實例來進行演示,本文程式碼的運行前提條件是你已經裝好了Redis6.x
版本,linux環境下可以直接從官網下載後編譯安裝,windows環境下的安裝可以參考 手摸手教你在Windows環境下運行Redis6.x 這篇文章。
概念上的東西我們也大體了解了,下面我們分別來看一下客戶端快取具體實現的三種模式(至於為什麼多了一種,後面再來細說)。在正式開始前,強烈建議大家先花個十幾分鐘了解一下 Redis6底層的通訊協議RESP3,否則在看到具體的通訊內容時可能會存在一些疑問。
首先做一下準備工作,通過telnet
連接redis服務,並切換到resp3
協議模式:
telnet 127.0.0.1 6379
hello 3
1、默認模式
在使用客戶端連接到redis服務後,需要先通過指令開啟tracking
模式的功能,因為在客戶端連接後這個選項是默認關閉的,會無法收到失效類型的push
消息:
#開啟
client tracking on
#關閉
client tracking off
當開啟tracking
後的默認模式下,redis服務端會記錄每個客戶端請求過的key,當key對應的值發生變化時,會發送失效資訊給客戶端。簡單總結一下,也就是說這個模式能夠生效的必要前提條件有兩個:
- 開啟
tracking
- 客戶端訪問過某個key
下面我們還是在telnet
中來模擬一下這個過程,分別啟動兩個redis客戶端,在client1中先執行get
命令後,再在client2對相同的key執行set
操作修改它的值,之後就會在client1中收到push
類型的消息。
push
類型的消息我們在RESP3中介紹過了,這裡簡單再嘮叨兩句:
>2
$10
invalidate
*1
$4
user
起始的第一位元組>
表示該消息為push
類型,後面消息體中包含了兩部分內容,第一部分表示收到的消息類型為invalidate
,也就是作廢類型的資訊,第二部分則是需要作廢的key是user
。
除此之外,當一個快取的key到達失效時間導致過期,或是因為到達最大記憶體,要使用驅逐策略進行驅逐時,也會對客戶端發送PUSH
的消息。下面以快取的key過期為例:
另外,對於單個key來說,這個tracking
消息只會對客戶端發送一次,當第二次修改該key所對應的值後,客戶端不會再收到tracking
的消息。只有對這個key再執行一次get
命令,之後才會再次收到tracking
消息。
默認模式雖然使用起來簡單,但是需要在服務端存儲客戶端的訪問數據,記錄哪些key被哪些客戶端訪問過。如果訪問的不是少量的熱點數據的話,可能會佔用大量redis服務端的記憶體空間。應對這種情況,可以試一試下面要介紹的廣播模式。
2、廣播模式
在廣播模式BCAST
下,redis服務端不再記錄key的訪問情況,而是無差別地向所有開啟tracking
廣播的客戶端發送消息。這樣一來,好處就是不需要浪費redis服務端的記憶體進行記錄,但是壞處就是客戶端可能會收到過多的消息,其中可能還會包含自己不需要的一些key。
在使用前,需要先通過命令開啟廣播模式:
client tracking on bcast
下面,我們通過一個例子來進行廣播模式的使用演示:
可以看到在開啟廣播模式後,只要在client2中修改了key對應的值,在client1中都會收到作廢消息,而不管client1之前在本地是否進行過快取。
並且,另外一點和默認模式不同的是,廣播模式是能夠重複多次收到一個key的失效消息的,因為服務端沒有記錄,所以只要有key發生了修改,客戶端就會收到失效消息。
這時候,有的小夥伴可能就要問了,如果我不想收到這麼多沒用的冗餘消息,有沒有什麼辦法進行一下過濾或精簡呢?
答案是可以的,在廣播模式下,客戶端可以只關注一些特定前綴的key,表示我只需要接收這些前綴的key,其他的就不要發給我了。命令格式如下:
client tracking on bcast prefix myprefix
再來看一下使用過程的示例:
可以看到,在設置了只關注以order:
作為前綴的key後,成功過濾掉了user
的失效消息。從這個角度來看,也要求了我們在快取一個類型的數據時,都以相同的單詞作為前綴,規範了我們在使用快取中對key的命名規則。
至於在業務中具體要使用哪種模式,可能更多的需要進行一下權衡。看一下你究竟是能忍受佔用更多redis服務端的記憶體,還是能夠忍受收到大量不需要的失效消息。
3、轉發模式
默認模式和廣播模式的生效,都要在開啟RESP3
協議的前提下,具體原因看過上面的例子大家應該也都清楚了,因為要使用tracking
的話,就必須要藉助到RESP3
協議中的新的push
消息類型。
那麼如果客戶端還是使用的舊版本RESP V2
的話,也想要體驗這一功能,應該如何進行改造呢?
不得不說redis6的開發者想的還是蠻全面的,為了適配RESP V2
,專門設計了一種新的轉發模式,允許使用舊版本協議的客戶端通過Pub/Sub
發布訂閱功能來接收key的失效資訊。
從上面這張圖可以看到,轉發模式的核心就是redis服務端會將原先push
類型的tracking
資訊,轉發到訂閱了_redis_:invalidate
這一信道的被指定的客戶端上。
我們來梳理一下上面的流程,首先在client1需要使用指令開啟轉發模式:
client tracking on bcast redirect [client-id]
相對廣播模式,多了兩個參數,redirect
表示為轉發模式,後面的client-id
表示消息要發送給哪一個客戶端,客戶端的id可以在client2上通過client id
指令獲取。
在client2中,則需要訂閱指定的信道:
subscribe _redis_:invalidate
其實說白了,轉發模式還是使用的發布訂閱功能罷了,只不過redis幫我們解放了雙手,把發送消息的工作由自己完成了。整個操作的流程如下圖所示:
可以看到,client2中收到的消息格式與之前的push
類型消息不同,是一條RESP V2中多條批量回復格式的消息,表示的含義同樣是收到的key已經作廢掉了。
需要注意的是,雖然說開啟轉發模式的指令中也帶了一個bcast
,但是它和廣播模式有著非常大的區別。在轉發模式下,key
的作廢消息只能被轉發到一個客戶端上,如果先後執行兩條指定轉髮指令,那麼後執行的指令會覆蓋前一指令中轉發的client-id
。
看到這裡是不是多少感覺這個轉發模式有點雞肋,畢竟實際的業務場景中很有可能會有多個客戶端的存在,只能轉發一個實在是有點說不過去了。不過,也有可能作者就是這麼設計,留點缺陷,好讓大家更快地擁抱RESP3
……
總結
好啦,到這裡客戶端快取的基本理論和使用就介紹的差不多了,不得不說,Redis6的這個新特性確實給了我們眼前一亮的感覺。從這個新特性也可以看出,Redis大有把快取從服務端的局限中掙脫出來,染指向客戶端,一統快取江湖的意味。
不過這個過程應該並不簡單,就像我們前面說的,畢竟只有Redis服務端還不夠,還需要優秀的客戶端進行支援才行。
那麼下一篇文章,我們就來從實戰角度,看看如何改造客戶端,讓client-side caching
能在項目中落地開花。
這次的分享就到這裡,我是Hydra,下篇文章再見。
官方文檔:
推薦閱讀
編譯實戰 | 手摸手教你在Windows環境下運行Redis6.x
Redis6通訊協議升級至RESP3,一口氣看完13種新數據類型
作者簡介,碼農參上,一個熱愛分享的公眾號,有趣、深入、直接,與你聊聊技術。個人微信DrHydra9,歡迎添加好友,進一步交流。