碼老溼,上次你講解了 Redis 多執行緒模型,這次我想知道客戶端快取(Client side caching)技術,他的英文名叫:Redis server-assisted client side caching
,可以說說麼?
我不是嫖客,看完我會點贊、再看、分享的。
別裝逼了,還整英文,咋不上天,做人要說話算數喲,不然半夜尿褲子。在說這個之前,碼哥先給讀者送一段寄語作為開篇。
開篇寄語
不要吝嗇你的讚美,如果別人做的很好,就給他正反饋,這也是一種利他。
另外,少關注用「讚美」投票的事物,而多關注用「交易」投票的事物。
判斷一個人是否牛逼,不是看網上有多少人讚美他,而是看有多少人願意跟他發生交易、讚賞、支付、下單。
因為讚美太廉價,而願意與他發生交易,才是真正的信任。
為啥需要客戶端快取
Redis
的Tracking Feature
的實現程式碼在: https://github.com/antirez/redis/blob/unstable/src/tracking.c
。
很多公司使用 Redis 做快取系統,並且很好的提高了資料訪問的效能,為了進一步應對熱點資料,還是會在 Redis 的 Client 端快取一部分熱點資料,用來應對「吃瓜事件」。
比如,「這該死的 996 福報」、「吳亦凡之大方牢房」、「時間管理大師」、「思聰舔我不得就錘我」、「吳秀波之談戀愛麼,能坐牢的那種」……
除了使用 Redis 快取避免直接訪問資料庫以外,還會加更多的 cache
層,比如採用 Memcachced
作為熱點資料的本地快取:
- 先去
Memcachced
中查詢資料,命中直接返回。 Memcachced
未命中,則再從 Redis 查詢,命中則返回資料,並在Memcachced
儲存這個資料。- Redis 未命中,則去
MySQL
中查詢,並依次設定到 Redis 和Memcachced
中。
訪問本地記憶體的的效能必然比通過網路訪問 Redis 快,所以這種模式可以極大地減少獲取資料的延遲,並且可以減少 Redis 的負載,提高效能。
- 訪問 Redis 獲取資料,伺服器響應。
使用客戶端快取,應用程式將獲取的熱門的資料儲存在用用程式中,無需再次通過網路訪問 Redis。
應該快取什麼
- 我們不應該快取不斷變化的鍵。
- 我們不該快取很少請求的鍵。
- 我們希望快取經常請求並以合理速率更改的鍵。對於沒有穩定變化速度的例子,比如不斷被
INCR
修改的全域性計數器,就不應該快取。
客戶端快取實現原理
碼老溼, Redis 中的資料修改或者失效了,如何及時同步告知客戶端失效了呢?自己實現也太複雜了。
Redis 實現的是一個服務端協助的客戶端快取,叫做tracking
。客戶端快取的命令是:
CLIENT TRACKING ON|OFF [REDIRECT client-id] [PREFIX prefix] [BCAST] [OPTIN] [OPTOUT] [NOLOOP]
Redis 6.0 實現 Tracking
功能提供了兩種模式解決這個問題,分別是使用RESP3 協議版本的普通模式和廣播模式,以及使用 RESP2 協議版本的轉發模式。
*
普通模式
當tracking
開啟時, Redis
會「記住」每個客戶端請求的 key
,當 key
的值發現變化時會傳送失效資訊給客戶端 (invalidation message
)。
失效資訊可以通過 RESP3
協議傳送給請求的客戶端,或者轉發給一個不同的連線 (支援 RESP2 + Pub/Sub
) 的客戶端。
- Server 端將 Client 訪問的 key以及該 key 對應的客戶端 ID 列表資訊儲存在全域性唯一的表(TrackingTable),當表滿了,回移除最老的記錄,同時觸發該記錄已過期的通知給客戶端。
- 每個 Redis 客戶端又有一個唯一的數字 ID,TrackingTable 儲存著每一個 Client ID,當連線斷開後,清除該 ID 對應的記錄。
- TrackingTable 表中記錄的 Key 資訊不考慮是哪個 database 的,雖然訪問的是 db1 的 key,db2 同名 key 修改時會客戶端收到過期提示,但這樣做會減少系統的複雜性,以及表的儲存資料量。
碼老溼,可以說下這個 TrackingTable 原理麼?
Redis 服務端使用 TrackingTable
儲存普通模式的客戶端資料,它的資料型別是基數樹 ( radix tree)。
基數樹是針對稀疏的長整型資料查詢的多叉搜尋樹,能快速且節省空間的完對映。
Redis 用它儲存鍵的指標和客戶端 ID 的對映關係。因為鍵物件的指標就是記憶體地址,也就是長整型資料。客戶端快取的相關操作就是對該資料的增刪改查:
注意
服務端對於記錄的 key 只會報告一次 invalidate 訊息,也就是說,服務端在給客戶端傳送過一次 invalidate 訊息後,如果 key 再被修改,此時,服務端就不會再次給客戶端傳送 invalidate 訊息。
只有下次客戶端再次執行只讀命令被 track,才會進行下一次訊息通知 。
客戶端預設不開啟 track 模式,我們需要在獲取執行指令之前執行開啟命令:
CLIENT TRACKING ON|OFF
+OK
GET user:211
$3
公眾號:碼哥位元組
廣播模式(BCAST)
當廣播模式 (broadcasting) 開啟時,伺服器不會記住給定客戶端訪問了哪些鍵,因此這種模式在伺服器端根本不消耗任何記憶體。
在這個模式下,服務端會給客戶端廣播所有 key 的失效情況,如果 key 被頻繁修改,服務端會傳送大量的失效廣播訊息,這就會消耗大量的網路頻寬資源。
所以,在實際應用中,我們設定讓客戶端註冊只跟蹤指定字首的 key,當註冊跟蹤的 key 字首匹配被修改,服務端就會把失效訊息廣播給所有關注這個 key字首的客戶端。
client tracking on bcast prefix user
這種監測帶有字首的 key 的廣播模式,和我們對 key 的命名規範非常匹配。我們在實際應用時,會給同一業務下的 key 設定相同的業務名字首,所以,我們就可以非常方便地使用廣播模式。
廣播模式與普通模式類似,Redis 使用 PrefixTable
儲存廣播模式下的客戶端資料,它儲存字首字串指標和(需要通知的 key 和客戶端 ID)的對映關係。
轉發模式
普通模式與廣播模式,需要客戶端使用 RESP 3 協議,他是 Redis 6.0 新啟用的協議。
對於使用 RESP 2 協議的客戶端來說,實現客戶端快取則需要另一種模式:重定向模式(redirect)。
RESP 2 無法直接 PUSH 失效訊息,所以 需要另一個支援 RESP 3 協議的客戶端 告訴 Server 將失效訊息通過 Pus/Sub 通知給 RESP 2 客戶端。
在重定向模式下,想要獲得失效訊息通知的客戶端,就需要執行訂閱命令 SUBSCRIBE,專門訂閱用於傳送失效訊息的頻道 _redis_:invalidate
。
同時,再使用另外一個客戶端,執行 CLIENT TRACKING 命令,設定服務端將失效訊息轉發給使用 RESP 2 協議的客戶端。
假設客戶端 B 想要獲取失效訊息,但是客戶端 B 只支援 RESP 2 協議,客戶端 A 支援 RESP 3 協議。我們可以分別在客戶端 B 和 A 上執行 SUBSCRIBE 和 CLIENT TRACKING,如下所示:
//客戶端B執行,客戶端 B 的 ID 號是 606
SUBSCRIBE _redis_:invalidate
//客戶端 A 執行
CLIENT TRACKING ON BCAST REDIRECT 606
B 客戶端就可以通過 _redis_:invalidate
頻道獲取失效訊息了。
熱門推薦