【譯】antirez:Redis6將支持客戶端緩存

本文翻譯自Redis做者antirez的一篇博客,原文地址是:antirez.com/news/130redis

紐約Redis日已經結束了,我仍然與意大利時區同步,早上5點30起牀,並當即走上了曼哈頓的街道,我很喜歡這裏的風景,而且享受着成爲這裏的一部分。當時我正在考慮發佈Redis 6的release版本,這是在將來一段時間最重要的事了。新版本的Redis協議(RESP3)推動得還很慢,若是沒有一個好的理由,明智的人是不會更換工具的。但我爲何要堅持提高協議呢?有兩個主要的緣由,一是須要給客戶端提供更加具備語義的回覆,二是提供一箇舊版本不能實現的新功能:客戶端緩存。緩存

時間倒回一年前,我到達聖安東尼奧的Redis Conf 2018。當時公司就有一個共識是客戶端緩存是Redis在將來很是重要的事情。若是咱們須要更快的存儲和更快的緩存,咱們就須要在客戶端存儲一部分信息。這是提供低延遲和大規模數據服務的很天然的想法。事實上,基本上每一個大公司也都是這樣作的,由於這是惟一的辦法。然而Redis沒有辦法在這一過程當中協助客戶。一個巧合讓Ben Malec想要在Redis Conf上作一些關於客戶端緩存的演講,他只使用Redis提供的工具和一些很是聰明的想法。bash

做者注:演講地址是https://www.youtube.com/watch?v=kliQLwSikO4服務器

Ben的演講啓發了我,爲了實現Ben的設計,其中有兩個關鍵點。第一個是使用Redis Cluster的「hash slot」的概念,把key分紅了16k個組。採用這種方式使得客戶端不須要追蹤每個key的位置,可使用一個簡單的元數據來定位key所在的group。Ben使用Pub/Sub模式來通知key的改變,因此他須要應用程序的一些幫助,然而這種模式是很固定的。要修改一個key?還須要發佈失效消息。在客戶端是否緩存了key呢?要記住緩存每一個key和收到失效消息時的時間戳,記住每一個slot的失效時間。當使用一個緩存的key時,先作一個懶清除,經過檢查緩存key的時間戳是否早於slot收到失效信息的時間戳。這種狀況下,這個key就是過期的數據,你能夠再次訪問服務器。函數

在看完演講以後,我意識到這是一個在服務器內使用的好主意,爲了讓Redis可以爲客戶端作一部分工做,是客戶端緩存更加簡單高效,因此我回到家寫下了個人設計文檔:groups.google.com/d/msg/redis…工具

但爲了實現個人設計,我必須專一於修改Redis協議使它變得更加完善,因此我開始編寫RESP3和Redis 6的其餘特性(好比ACL)的規範和代碼,客戶端緩存是Redis許多迭代想法中的一種,有些想法由於時間不夠放棄了。google

當時我在紐約街頭思考這個想法。後來和一些朋友去吃午餐喝咖啡。當我返回酒店房間後,距離次日起飛還有一整晚的時間,因此我開始按照一年前寫的提案來寫Redis 6的客戶端緩存的實現。spa

Redis服務器助理客戶端緩存,最終叫作「tracking」(我也可能改主意),是一個由幾個關鍵想法組成的很是簡單功能。線程

key空間被分割到」caching slots「,但他們比Ben使用的hash slots要多得多。咱們使用CRC64的24位輸出,因此有超過1600萬個不一樣的slot。爲何這麼多呢?由於我認爲你想要有一個1億key的服務器。然而一個失效信息影響的key不該該多於客戶端緩存中的key。Redis中失效表佔用130M的內存:8字節的指針指向16M的條目。這對我來講是能夠接受的,若是你想要使用新功能,你將充分利用你在客戶端的全部內存,因此使用130MB在服務器端是好的,你能夠得到更細粒度的失效。翻譯

客戶端使用「opt in」方法開啓這個功能,只須要一個簡單的命令:

CLIENT TRACKING on
複製代碼

服務器老是返回+OK,從這時起,每一個命令都在命令表中被標記爲「只讀」,再也不給調用者返回keys,並記住客戶端請求的全部的key。保存這種信息時很是簡單的,每一個Redis客戶端都有本身的惟一ID,因此若是ID是123的客戶端發送了MGET命令,須要從slot 1,2和5獲取key,那麼失效表中咱們就須要記錄以下信息:

1 -> [123]
2 -> [123]
5 -> [123]
複製代碼

接着,ID爲444的客戶端也須要到slot5請求key了,那麼表信息將變成:

5 -> [123,444]
複製代碼

如今其餘客戶端修改了slot 5中的某個key,Redis將會檢查失效表,發現客戶端123和444都緩存了這個slot上的key。咱們將會給這些客戶端發送失效信息,而後會記錄下slot最後的失效時間戳,並在之後懶檢查緩存對象的時間戳,並對照後判斷是否失效。此外,客戶端能夠回收表中緩存的指定slot的對象。這種具備24位hash函數的方法不是問題,由於咱們即便緩存幾千萬的key,也不會有很長的列表。發送了失效信息後,咱們就能夠刪除失效表中的項,這樣直到這些客戶端再也不讀這些slot的key,咱們就再也不向他們發送失效消息。

須要注意的是,客戶端沒必要強制使用24位hash函數。也可能使用20位,而後移動Redis發送的失效消息的slot。不肯定是否有不少很好的理由這樣作,可是內存受限時,這多是一種想法。

若是你密切關注我說的話,你會開始考慮同一鏈接既會接收到正常的客戶端回覆,又會接收失效消息。這能夠經過RESP3實現,由於失效做爲「推送」消息類型發送。若是客戶端是一個阻塞類型的,而且不是事件驅動類型的客戶端,就會變得比較複雜:

應用程序須要一些方法來不時讀取新數據,這看起來既複雜又脆弱。在這種狀況下,爲了接收失效消息,使用另外一個應用程序線程和不一樣的客戶端可能會更好。因此你可使用如下命令來容許這樣的操做:

CLIENT TRACKING on REDIRECT 1234
複製代碼

基本上咱們能夠說咱們使用當前鏈接得到的全部key,並但願失效消息發送到客戶端1234。在鏈接池的狀況下多個客戶端可能會要求將失效消息重定向到單個客戶端。你須要作的就是建立特殊鏈接以接收失效消息,調用CLIENT ID以瞭解此客戶端鏈接哪一個ID,而後啓用跟蹤。

如今只剩下一個問題了:若是咱們失去了失效鏈接怎麼辦?咱們可能由於不能接收到失效消息而陷入麻煩。一般,應用會檢測鏈接,嘗試重連,並清除緩存。爲了確保失效鏈接處於鏈接狀態,不時地向服務器發送ping請求多是一個更好的主意。然而,爲了下降過時數據的風險,Redis也將開始通知客戶端將失效消息重定向到其餘客戶端,只要使用特殊的推送消息:下一個請求就會使客戶端知道鏈接已經斷開。

我剛纔描述的已經合併到Redis的unstable分支。可能不是最終的處理方法,可是在第一個Redis 6發佈版本以前還有幾個月的時間,咱們還有時間修改全部的事情:能夠告訴我你的反饋。我也會再尋找其餘RESP2可行的方法。這隻有在重定向開啓時纔有效,而且客戶端要進入Pub/Sub模式監聽消息。經過這種方式,徹底能夠複用舊客戶端。

我但願這足以刺激你的胃口:若是咱們在Redis中運行的很好,而後記錄下來,讓客戶端做者知道該如何支持,數據可能比以往更接近應用程序,甚至在小型團隊運行的應用程序中,到目前爲止尚未嘗試客戶端緩存。對於正在準備作的大型團隊和很是大的應用程序,下降實現成本和複雜性。

相關文章
相關標籤/搜索