RedisConf2020之Redis客戶端緩存

來(lái)源:Ben Malec from Paylocity and RedisConf 2020 organized by Redis Labs

翻譯:Wen Hui

轉(zhuǎn)載:中間件小哥

這篇文章中我們介紹如何使用Redis 6中關(guān)于客戶端緩存的支持來(lái)設(shè)計(jì)我們的客戶端緩存機(jī)制。我們首先來(lái)看一個(gè)典型的web應(yīng)用如下:

image

在loadbalancer后面我們有多個(gè)web服務(wù)器尤揣,并與相同的SQL數(shù)據(jù)庫(kù)相連接缆镣。另外,在每個(gè)web服務(wù)器中躁倒,我們有多個(gè)服務(wù)器端緩存用來(lái)在服務(wù)器端緩存SQL數(shù)據(jù)庫(kù)中的數(shù)據(jù)。

我們這樣設(shè)計(jì)的目的是避免每次數(shù)據(jù)讀操作都訪問數(shù)據(jù)庫(kù)從而帶來(lái)較高的系統(tǒng)延時(shí)。但是,這種設(shè)計(jì)模式帶來(lái)一個(gè)主要問題是如果其中一個(gè)web服務(wù)器接收到更新數(shù)據(jù)的請(qǐng)求谋旦,會(huì)更新數(shù)據(jù)庫(kù)中的數(shù)據(jù),以及這個(gè)服務(wù)器中的服務(wù)器緩存,但是其他的web服務(wù)器中會(huì)繼續(xù)緩存舊的數(shù)據(jù)册着,從而帶來(lái)數(shù)據(jù)不一致的問題拴孤。我們可以想到有多個(gè)解決方案來(lái)解決這個(gè)問題,首先一種比較常見的方案是更新數(shù)據(jù)的web服務(wù)器可以將數(shù)據(jù)更新的請(qǐng)求廣播到其他的web服務(wù)器中甲捏。但是這種方式會(huì)帶來(lái)以下兩個(gè)主要問題:

1. 會(huì)很大程度上增加系統(tǒng)網(wǎng)絡(luò)的負(fù)載演熟。

2. 會(huì)導(dǎo)致競(jìng)態(tài)條件(race condition)以及其他一些問題。例如如果兩個(gè)web服務(wù)器同時(shí)更新同一個(gè)數(shù)據(jù)司顿,那么系統(tǒng)網(wǎng)絡(luò)無(wú)法保證這兩個(gè)更新請(qǐng)求到達(dá)其他服務(wù)器的先后順序绽媒。

針對(duì)以上問題,一個(gè)常見的解決方案是我們可以將服務(wù)器端緩存替換成Redis免猾,如下圖所示:

image

這樣做的好處是:

1. Redis比其他后端數(shù)據(jù)庫(kù)存儲(chǔ)要快許多。

2. 完全可以解決競(jìng)態(tài)條件或數(shù)據(jù)不一致的問題囤热,因?yàn)槎鄠€(gè)web服務(wù)器共享了redis實(shí)例猎提,所以客戶端每次都會(huì)得到正確的結(jié)果。

但這樣也帶來(lái)了web服務(wù)器與Redis通信的網(wǎng)絡(luò)延時(shí)的問題旁蔼。因?yàn)樵谝话闱闆r下锨苏,內(nèi)存訪問速度很快,所以網(wǎng)絡(luò)延時(shí)很容易成為這種設(shè)計(jì)的瓶頸棺聊。

如果我們綜合了以上兩種設(shè)計(jì)伞租,我們會(huì)得到以下設(shè)計(jì)方案:

image

在這里,我們保留服務(wù)器端緩存限佩,并在redis中數(shù)據(jù)改變的情況下葵诈,通過redis的廣播機(jī)制來(lái)更新其他web服務(wù)器中的緩存。整體設(shè)計(jì)如下:

1. Redis始終是系統(tǒng)緩存的單一數(shù)據(jù)源祟同。

2. 當(dāng)Redis緩存的值更新后作喘,通過Redis的發(fā)布訂閱連接來(lái)更新其他web服務(wù)器中的緩存。

3. 在其他web服務(wù)器收到更新緩存的消息以后晕城,會(huì)使本地服務(wù)器端緩存失效泞坦,然后再下次接收到讀數(shù)據(jù)請(qǐng)求的時(shí)候訪問redis實(shí)例以拿到更新后的值。

4. 這樣設(shè)計(jì)不僅可以保持?jǐn)?shù)據(jù)一致性并且可以使用服務(wù)器端緩存以降低系統(tǒng)延時(shí)砖顷。

5. 在多個(gè)服務(wù)器同時(shí)更新的情況下贰锁,因?yàn)镽edis是單一數(shù)據(jù)源,所以所有的服務(wù)器端緩存都會(huì)得到正確的值滤蝠。

6. Paylocity做了進(jìn)一步的優(yōu)化豌熄,沒有將整個(gè)鍵來(lái)廣播給其他web服務(wù)器,而是只廣播鍵的hash值几睛,這樣可以進(jìn)一步降低網(wǎng)絡(luò)負(fù)載房轿。但同時(shí)也增加一些程序的復(fù)雜度。

作者在Redis Conf 2018 對(duì)這種解決方案做了演講,但也收到Redis 作者Savatore提到的一些潛在的問題:

1. 所有的更新操作必須通過web服務(wù)器中的緩存邏輯囱持,如果直接更改redis的話其他web服務(wù)器端不會(huì)收到更新夯接。

2. 所有更新都會(huì)被廣播到所有的web服務(wù)器中,無(wú)論web服務(wù)器中是否緩存過更新的鍵纷妆。

3. 如果Redis內(nèi)核可以為客戶端緩存的逐出提供幫助將是一個(gè)更好的方案盔几。

到了Redis 6.0版本,redis實(shí)現(xiàn)了針對(duì)客戶端緩存的追蹤機(jī)制掩幢,具體特性如下:

1. 在Redis中添加了關(guān)于客戶端緩存追蹤的新命令逊拍。

2. Opt in:Redis客戶端可以選擇是否啟用客戶端追蹤。

3. 兩種模式: 默認(rèn)和廣播模式际邻。

4. Redis會(huì)記錄鍵值的改動(dòng)芯丧,并記錄哪個(gè)客戶端對(duì)哪個(gè)鍵值感興趣。

5. 當(dāng)鍵值改動(dòng)后世曾,Redis會(huì)發(fā)送給啟用緩存追蹤的客戶端發(fā)送緩存無(wú)效信息缨恒。

6. 客戶端會(huì)逐出特定的客戶端緩存,下一次的請(qǐng)求將訪問Redis以獲取數(shù)據(jù)轮听。

下面具體介紹Redis 客戶端緩存追蹤的具體模式:

1. 默認(rèn)模式

Redis 命令:CLIENT TRACKING ON

在默認(rèn)模式下, Redis會(huì)顯式的記住每個(gè)客戶端感興趣哪個(gè)特定的鍵骗露,如果鍵被更新時(shí),Redis只給那些對(duì)這個(gè)鍵感興趣的客戶端發(fā)送緩存無(wú)效的信息血巍。

優(yōu)點(diǎn):

可以最大限度利用網(wǎng)絡(luò)帶寬萧锉,不會(huì)有多余的消息發(fā)送給沒有緩存過這個(gè)鍵的客戶端。

缺點(diǎn):

對(duì)于Redis服務(wù)器來(lái)說記住每個(gè)客戶端感興趣的鍵會(huì)導(dǎo)致使用更多的內(nèi)存述寡。

如果我們有幾千個(gè)客戶端和幾百萬(wàn)個(gè)鍵柿隙,這種方式會(huì)消耗非常大的內(nèi)存資源。

如果redis需要清理追蹤部分的內(nèi)存的話辨赐,需要給客戶端發(fā)送緩存無(wú)效的消息优俘, 即使特定的鍵值沒有被改變。

2. 廣播模式

Redis命令: CLIENT TRACKING ON BCAST

在廣播模式下掀序,Redis會(huì)發(fā)送給啟用客戶端緩存追蹤的所有客戶端發(fā)送緩存失效消息帆焕,無(wú)論特定的鍵是否緩存在客戶端中。

優(yōu)點(diǎn):

沒有在Redis服務(wù)器實(shí)例中顯著使用內(nèi)存不恭。

缺點(diǎn):

需要更多的網(wǎng)絡(luò)帶寬叶雹。

3. 帶注冊(cè)前綴的廣播模式

Redis命令: CLIENT TRACKING ON BCAST PREFIX key_prefix_value

在廣播模式下,可以通過注冊(cè)鍵前綴方式來(lái)限制在鍵被更新的情況下换吧,只有注冊(cè)特定鍵前綴的客戶端才會(huì)收到緩存失效的消息折晦。

需要注意的地方是:

1. 鍵的名字需要仔細(xì)定義,因?yàn)檫@樣可以減少緩存失效消息的數(shù)量沾瓦。

2. 多個(gè)鍵前綴可以被同個(gè)客戶端指定满着。

3. 可以通過鍵前綴來(lái)告訴redis哪些鍵會(huì)被客戶端緩存而那些不會(huì)谦炒,例如:

clientSide:MyAppCode:keyname和MyAppCode:keyname。

4.默認(rèn)模式中的Optin 模式

Redis命令:CLIENT TRACKING ON OPTIN

在Opt in模式下风喇,客戶端將收到所指定的鍵緩存失效的消息宁改,在默認(rèn)模式中,所有鍵是默認(rèn)注冊(cè)的魂莫,但在Opt in模式下还蹲,需要顯式指定哪些鍵需要被注冊(cè)。指定鍵被注冊(cè)的命令是CLIENT CACHING YES耙考,接下來(lái)的一個(gè)讀請(qǐng)求的鍵將會(huì)被注冊(cè)在Redis服務(wù)器端的追蹤表里谜喊,當(dāng)這個(gè)鍵被更新時(shí)客戶端會(huì)收到緩存失效消息。

例子如下:

CLIENT CACHING YES

+OK

GET MYKEY1

$8

MyValue6

現(xiàn)在如果MYKEY1的值被改變倦始,則客戶端會(huì)收到緩存失效消息斗遏。

5 默認(rèn)模式中的Opt Out模式

Redis 命令: CLIENT TRACKING ON OPTOUT

和OPTIN 模式相反,在OPTOUT模式下客戶端鍵默認(rèn)會(huì)被追蹤鞋邑,需要顯式指定哪些鍵不需要被追蹤最易。

如果需要關(guān)閉特定鍵的追蹤,需要向Redis發(fā)送讀請(qǐng)求之前使用CLIENT CACHING NO命令炫狱。

例子如下:

CLIENT CACHING NO

+OK

GET MYKEY2

$8

MyValue2

現(xiàn)在如果MYKEY2的值被改變,客戶端不會(huì)收到緩存失效的消息剔猿。

在介紹完客戶端緩存追蹤的幾種模式后视译,下一個(gè)問題就是在何種情況下需要使用何種模式。這個(gè)問題實(shí)際上跟具體的應(yīng)用需求有關(guān)归敬,實(shí)際上是在Redis內(nèi)存使用和整個(gè)系統(tǒng)網(wǎng)絡(luò)資源上做取舍酷含。

你的應(yīng)用是否有很多緩存更新的場(chǎng)景?如果不是的話可以選用廣播模式汪茧,因?yàn)樵谶@種場(chǎng)景下大部分的鍵不會(huì)被更新椅亚,所以沒有必要在Redis端記住所有的鍵信息。

相反的舱污,如果應(yīng)用相對(duì)來(lái)說有很多更新緩存鍵的應(yīng)用場(chǎng)景呀舔,那么可以選用默認(rèn)模式,尤其當(dāng)有很多客戶端扩灯,但每個(gè)客戶端緩存鍵的數(shù)量相對(duì)較少的情況下媚赖。默認(rèn)模式是最好的選擇。

另外在我們的設(shè)計(jì)中珠插,我們會(huì)把CLIENT ID 也放到緩存無(wú)效信息中惧磺,這樣特定客戶端會(huì)忽略自己觸發(fā)的的緩存失效消息。(注在新版本的Redis 6.0.4中捻撑,可以使用NOLOOP來(lái)達(dá)到同樣的效果)

另外需要留意的是客戶端追蹤可以指定另一個(gè)REDIRECT選項(xiàng)磨隘,這個(gè)選項(xiàng)主要是為RESP2(舊版本)協(xié)議的客戶端提供的缤底。因?yàn)樵谂f版本的協(xié)議中,不支持從Redis服務(wù)器端向客戶端推送消息番捂。所以這個(gè)選項(xiàng)的作用是將緩存失效的消息推送到額外指定的客戶端个唧。

具體的使用方法是:

1. 在客戶端啟用客戶端緩存前,創(chuàng)建一個(gè)新的客戶端白嘁,使用CLIENT ID命令記錄下這個(gè)客戶端的ID坑鱼,讓后使用SUBSCRIBE redis:invalidate來(lái)訂閱緩存失效消息的頻道。

2. 在啟用客戶端緩存的時(shí)候使用這個(gè)客戶端ID來(lái)接收無(wú)效消息絮缅,例如如果ID為5鲁沥,則使用CLIENT TRACKING YES REDIRECT 5。

3. 這樣緩存無(wú)效消息將發(fā)送到這個(gè)指定客戶端中耕魄,但是需要注意的是這種方式會(huì)潛在帶來(lái)潛在的競(jìng)態(tài)條件画恰。因?yàn)槿绻瑫r(shí)有另外客戶端更新Redis中的鍵數(shù)據(jù),無(wú)法保證客戶端收到緩存失效消息的時(shí)間吸奴。

在新版本的Redis RESP3協(xié)議中支持從Redis服務(wù)器端推送消息允扇,但在RESP2協(xié)議中只支持客戶端發(fā)送請(qǐng)求Redis服務(wù)器端處理并回復(fù)。

1. 現(xiàn)在客戶端可以使用兩種不同版本的協(xié)議则奥。

2. 不需要?jiǎng)?chuàng)建新的TCP連接來(lái)接收緩存失效消息考润。創(chuàng)建新的連接對(duì)Paylocity來(lái)說是一個(gè)很大的問題,因?yàn)槲覀兪褂枚鄠€(gè)Redis集群读处,每個(gè)客戶端保留向集群中每一個(gè)節(jié)點(diǎn)的連接糊治。

3. 但現(xiàn)在RESP3協(xié)議的主要問題是大部分的客戶端庫(kù)都沒有支持這個(gè)協(xié)議。

下面我們討論使用客戶端追蹤的應(yīng)用具體實(shí)現(xiàn)步驟:

1. 在客戶端中罚舱,創(chuàng)建客戶端緩存并連接Redis

2. 使用CLIENT TRACKING ON 來(lái)啟用追蹤井辜。

3. 更新數(shù)據(jù)時(shí),更新Redis 和相應(yīng)的客戶端緩存管闷。需要記住的是Redis是單一數(shù)據(jù)來(lái)源粥脚。

4. 在讀取數(shù)據(jù)過程中,先從客戶端緩存中讀取包个,如果未找到數(shù)據(jù)則在Redis中讀取刷允。如果在Redis中找到數(shù)據(jù),則更新客戶端本地緩存碧囊,這樣的話下個(gè)請(qǐng)求將會(huì)通過本地緩存拿到新的數(shù)據(jù)恃锉。同時(shí)也需要從Redis中讀取TTL并設(shè)置本地緩存過期時(shí)間。

5. 監(jiān)聽連接的無(wú)效消息頻道呕臂,如果客戶端接收到Redis發(fā)送的緩存失效消息破托,則更新本地緩存。

Paylocity的計(jì)劃:

利用Redis的客戶端追蹤功能:

像之前介紹的那樣歧蒋,我們有比較少的web服務(wù)器土砂,而且在大多數(shù)情況下鍵值不會(huì)經(jīng)常改變州既,所以廣播模式比較適用于這種場(chǎng)景。

廣播前綴的方式非常適用于我們的場(chǎng)景萝映,因?yàn)槲覀儠?huì)創(chuàng)建一個(gè)大的Redis集群并共享給各個(gè)應(yīng)用吴叶。所以我們使用應(yīng)用代號(hào)來(lái)當(dāng)作BROADCAST的前綴。

使用RESP3客戶端:

1. 可以極大的減少總的Redis連接數(shù)

2. 目前我們使用StackExchange Redis客戶端序臂,一個(gè)非常好的客戶端蚌卤,只是不確定以后對(duì)RESP3協(xié)議的支持。

3. 如果我們自己實(shí)現(xiàn)redis客戶端的話也不會(huì)有大的問題奥秆,因?yàn)槲覀冎恍枰С諫ET,SET 和接收緩存失效消息逊彭。

以下作者提供了使用Redis客戶端緩存支持的例子:

https://github.com/bmalec/RedisClientTrackingDemo

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市构订,隨后出現(xiàn)的幾起案子侮叮,更是在濱河造成了極大的恐慌,老刑警劉巖悼瘾,帶你破解...
    沈念sama閱讀 222,252評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件囊榜,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡亥宿,警方通過查閱死者的電腦和手機(jī)卸勺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)烫扼,“玉大人孔庭,你說我怎么就攤上這事〔闹耄” “怎么了?”我有些...
    開封第一講書人閱讀 168,814評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵怎抛,是天一觀的道長(zhǎng)卑吭。 經(jīng)常有香客問我,道長(zhǎng)马绝,這世上最難降的妖魔是什么豆赏? 我笑而不...
    開封第一講書人閱讀 59,869評(píng)論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮富稻,結(jié)果婚禮上掷邦,老公的妹妹穿的比我還像新娘。我一直安慰自己椭赋,他們只是感情好抚岗,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,888評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著哪怔,像睡著了一般宣蔚。 火紅的嫁衣襯著肌膚如雪向抢。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,475評(píng)論 1 312
  • 那天胚委,我揣著相機(jī)與錄音挟鸠,去河邊找鬼。 笑死亩冬,一個(gè)胖子當(dāng)著我的面吹牛艘希,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播硅急,決...
    沈念sama閱讀 41,010評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼覆享,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了铜秆?” 一聲冷哼從身側(cè)響起淹真,我...
    開封第一講書人閱讀 39,924評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎连茧,沒想到半個(gè)月后核蘸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,469評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡啸驯,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,552評(píng)論 3 342
  • 正文 我和宋清朗相戀三年客扎,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片罚斗。...
    茶點(diǎn)故事閱讀 40,680評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡徙鱼,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出针姿,到底是詐尸還是另有隱情袱吆,我是刑警寧澤,帶...
    沈念sama閱讀 36,362評(píng)論 5 351
  • 正文 年R本政府宣布距淫,位于F島的核電站绞绒,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏榕暇。R本人自食惡果不足惜蓬衡,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,037評(píng)論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望彤枢。 院中可真熱鬧狰晚,春花似錦、人聲如沸缴啡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)业栅。三九已至讨衣,卻和暖如春换棚,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背反镇。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工固蚤, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人歹茶。 一個(gè)月前我還...
    沈念sama閱讀 49,099評(píng)論 3 378
  • 正文 我出身青樓夕玩,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親惊豺。 傳聞我的和親對(duì)象是個(gè)殘疾皇子燎孟,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,691評(píng)論 2 361