社區(qū)收藏緩存設(shè)計(jì)重構(gòu)實(shí)戰(zhàn)

一略吨、背景

社區(qū)收藏業(yè)務(wù)是一個典型的讀多寫少的場景屏富,社區(qū)各種核心Feeds流都需要依賴用戶是否收藏的數(shù)據(jù)判斷驯耻,早期緩存設(shè)計(jì)時(shí)由于流量不是很大发皿,未體現(xiàn)出明顯的問題崔慧,近期通過監(jiān)控平臺等相關(guān)手段發(fā)現(xiàn)了相關(guān)的一些問題,因此我們針對這些問題對緩存做了重構(gòu)設(shè)計(jì)穴墅,以保障收藏業(yè)務(wù)的性能和穩(wěn)定性惶室。

二、問題分析定位

2.1 接口RT偏大

通過監(jiān)控平臺查看「判斷是否收藏接口」的RT在最高在8ms左右玄货,該接口的主要作用是判斷指定單個用戶是否已收藏一批內(nèi)容皇钞,其實(shí)如果緩存命中率高的話,接口RT就應(yīng)該趨近于Redis的RT水平松捉,也就是1-2ms左右夹界。


image.png
image.png

2.2 Redis&MySQL訪問QPS偏高

通過監(jiān)控平臺可以看到從上游服務(wù)過來的收藏查詢QPS相對訪問Redis緩存的QPS放大了15倍,并且MySQL查詢的最高QPS占上游訪問量接近37%隘世,這說明緩存并沒有很高的命中率可柿,導(dǎo)致回表查詢的概率還是很大。

QPS訪問量見下圖:

Redis訪問量

image.png

MySQL訪問量

image.png

基于以上分析我們現(xiàn)在有了明確的優(yōu)化切入點(diǎn)丙者,接下來我們來看下具體的找下原因是什么复斥。

接下來我們來看一下偽代碼的實(shí)現(xiàn):

//判斷用戶是否對指定的動態(tài)收藏
func IsLightContent(userId uint64,contentIds []uint64){
    index := userId%20
    cacheKey := key + "_" + fmt.Sprintf("%d", index)
    pipe := redis.GetClient().Pipeline()
    for _, item := range contentIds {
        InitCache(userId, contentId)
        pipe.SisMember(cacheKey, userId)
    }
    pipe.Exec()
    //......
}

//緩存初始化判斷,不存在則初始化數(shù)據(jù)緩存
func InitCache(userId uint64,contentId uint64){
    index := userId%20
    cacheKey := key + "_" + fmt.Sprintf("%d", index)
    ttl,_ := redis.GetClient().TTL(cacheKey)
    if ttl <= 0{//key不存在或者未設(shè)置過期時(shí)間
        // query from db
        // sql := "select userId from trendFav where userId%20 = index and content_id = contentId"
        // save to redis
    }else{
       redis.GetClient().Expire(cacheKey,time.Hour()*48)
    }
}

從上面的偽代碼中械媒,我們能夠很清晰的看到目锭,該方法會遍歷內(nèi)容id集合,然后對每個內(nèi)容去查詢緩存下來的用戶集合纷捞,判斷該當(dāng)前用戶是否收藏痢虹。也就是說緩存設(shè)計(jì)是按照內(nèi)容維度和用戶1:N來設(shè)計(jì)的,將單個動態(tài)下所有收藏過內(nèi)容的用戶id查出來緩存起來主儡。并且基于大Key的考慮世分,代碼又將用戶集合分片成20組。這無疑又再次放大了Redis緩存Key的數(shù)量缀辩。并且每個Key都使用TTL命令來判斷是否過期臭埋。這樣一來Redis的QPS和緩存Key就會被放大很多倍。

正是由于分片策略+緩存時(shí)效短臀玄,導(dǎo)致了MySQL查詢的QPS居高不下瓢阴。

三、解決方案

基于以上對問題的分析定位健无,我們思考的解決思路就是一次接口請求降低Redis查詢操作荣恐,盡可能減少放大的情況,初步判斷有如下兩個實(shí)現(xiàn)路徑:

  • 去掉遍歷內(nèi)容查詢,改為一次性查詢

  • 去掉用戶集分片存儲叠穆,改為單Key存儲

上游的調(diào)用參數(shù)用戶和內(nèi)容是一對多的關(guān)系少漆,因此要實(shí)現(xiàn)的Redis查詢也是要滿足一對多的關(guān)系,那么顯而易見我們的緩存應(yīng)該是按照用戶的維度來存儲已經(jīng)收藏過的內(nèi)容集合硼被。

用戶收藏的內(nèi)容比較少的話示损,我們很簡單的就可以從數(shù)據(jù)庫全部查詢出來放在緩存,但如果用戶收藏的內(nèi)容比較多呢嚷硫,那也會可能造成大Key問題检访,如果繼續(xù)分片存儲的話又會回到了原來的方案。我們討論出以下兩種方案:

方案1. 處理大數(shù)據(jù)大部分常規(guī)思路就是要么分片仔掸,要么冷熱分離

因?yàn)闃I(yè)務(wù)邏輯的特點(diǎn)脆贵,推薦流下用戶看到的內(nèi)容絕大部份基本都是一年以內(nèi)的,我們可以緩存用戶一年以內(nèi)的收藏內(nèi)容起暮,這樣就限制了用戶收藏的極端數(shù)量卖氨。如果看到的內(nèi)容發(fā)布超過一年時(shí)間,可以用MySQL直接查詢负懦,這種場景的case概率是很小的筒捺。但仔細(xì)考慮了下實(shí)現(xiàn),這個需要依賴業(yè)務(wù)方密似,我們需要去查詢內(nèi)容的發(fā)布時(shí)間,以此來判斷是否在我們的緩存內(nèi)葫盼,這樣會加重整個接口的邏輯残腌,反而得不償失,因此該思路很快就被否定了贫导。

方案2. 既然不能依賴第三方抛猫,就是要從自身擁有的信息上,來能夠緩存一部分最熱的數(shù)據(jù)孩灯,使得查詢能夠大范圍落到這些數(shù)據(jù)

我們目前只有內(nèi)容id闺金,而內(nèi)容id都是純數(shù)字,數(shù)字本身的話可以按照大小來排列峰档。業(yè)務(wù)查詢本身都是最近一段時(shí)間的內(nèi)容败匹,所以查詢的內(nèi)容id都是近期較大的id。那我們可以按照內(nèi)容id降序排列讥巡,取用戶收藏過的若干條數(shù)據(jù)來緩存掀亩。只要查詢的id都比緩存最小的id大,那么我們就可以只通過緩存來判斷出用戶是否收藏這些內(nèi)容了欢顷。

示例:

初始化緩存時(shí)我們按照內(nèi)容id降序排列槽棍,拿到前5000個內(nèi)容id:

  1. 如果查詢結(jié)果不滿5000,那么這個用戶緩存了全部收藏記錄,此時(shí)小緩存的內(nèi)容id為0

  2. 如果大于等于5000炼七,說明還有部分未緩存的記錄缆巧,此時(shí)最小緩存的內(nèi)容id為第5000個內(nèi)容ID

等到查詢判斷時(shí),將查詢的內(nèi)容id數(shù)組和緩存的最小內(nèi)容id對比豌拙,如果全部大于陕悬,則說明都在緩存范圍內(nèi),如果有小于姆蘸,則是超過緩存范圍墩莫,屆時(shí)單獨(dú)去數(shù)據(jù)庫判斷,當(dāng)然這種概率在業(yè)務(wù)上的發(fā)生幾率是比較小的逞敷。

這里緩存的數(shù)量的抉擇顯得尤為重要狂秦,如果太小,那緩存的命中率不高推捐,導(dǎo)致MySQL回表查詢概率變大裂问,如果太大,則初始化時(shí)比較耗費(fèi)時(shí)間牛柒,或產(chǎn)生大Key問題堪簿。經(jīng)過分析線上數(shù)據(jù),目前以5000這個數(shù)字能夠比較好的權(quán)衡皮壁。

下面是查詢緩存判斷流程圖:

image.png

緩存方式由原來的set結(jié)構(gòu)椭更,改為Hash結(jié)構(gòu),TTL延長到7 * 24 hour蛾魄。

image.png

這樣一來平委,原來的獨(dú)立調(diào)用的TTL和sismember命令洼畅,可以合并成一個Hmget命令,減少了一半的Redis訪問次數(shù),這個改進(jìn)收益是相當(dāng)可觀的劳曹。

四臊泰、優(yōu)化成果

截止本文撰寫時(shí)舞骆,我們對收藏的功能進(jìn)行了優(yōu)化改造并上線曹步,取得了很不錯的進(jìn)展。所有數(shù)據(jù)為最近7天的數(shù)據(jù)4.14 - 4.20,優(yōu)化效果在4.15號17點(diǎn)左右開始魔市。

4.1 RPC接口響應(yīng)RT降低

1 IsCollectionContent

RPC接口主届,判斷動態(tài)是否緩存。平均RT提高了接近3倍待德。并且RT比較穩(wěn)定

image.png

4.2 Redis負(fù)載降低

1 TTL 查詢

查詢Key有效期岂膳,用來判斷延長Key有效期。QPS直接降到0

image.png

2 SISMEMBER查詢

原來舊的收藏緩存查詢磅网,已經(jīng)改為HMGET查詢QPS降低到0

image.png

3 HMGET查詢

新的收藏緩存查詢QPS數(shù)量和上游過來查詢的QPS正好能對應(yīng)上

image.png

4 Redis 內(nèi)存降低

新的緩存較舊緩存在占用內(nèi)存和Key數(shù)量這2個指標(biāo)均降低了3倍左右

4.3 MySQL負(fù)載降低

1 content_collection表select查詢降低

QPS降低了24倍左右并且保持在一個比較穩(wěn)定的水位

image.png

2 MySQL連接并發(fā)數(shù)降低

查詢QPS的減少也降低了并發(fā)連接數(shù)谈截,大概降低了3倍左右,最終也降低了等待連接次數(shù)

image.png
image.png

五、總結(jié)

經(jīng)過對本次問題的分析和解決簸喂,不難看出一個良好的緩存設(shè)計(jì)對于服務(wù)來說是多么的重要毙死。好的緩存設(shè)計(jì)不僅能夠提升性能,同時(shí)可以降低資源使用喻鳄,整體提升了資源利用率扼倘。同時(shí)下游的流量和上游基本持平,在流量上升時(shí)除呵,不會對下游造成很大的壓力再菊,這樣服務(wù)整體的抗并發(fā)能力也提升了很多。

*文/Sky

關(guān)注得物技術(shù)颜曾,每周一三五晚18:30更新技術(shù)干貨
要是覺得文章對你有幫助的話纠拔,歡迎評論轉(zhuǎn)發(fā)點(diǎn)贊~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市泛豪,隨后出現(xiàn)的幾起案子稠诲,更是在濱河造成了極大的恐慌,老刑警劉巖诡曙,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件臀叙,死亡現(xiàn)場離奇詭異,居然都是意外死亡价卤,警方通過查閱死者的電腦和手機(jī)劝萤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來慎璧,“玉大人床嫌,你說我怎么就攤上這事≌ū埃” “怎么了既鞠?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵煤傍,是天一觀的道長盖文。 經(jīng)常有香客問我,道長蚯姆,這世上最難降的妖魔是什么五续? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮龄恋,結(jié)果婚禮上疙驾,老公的妹妹穿的比我還像新娘。我一直安慰自己郭毕,他們只是感情好它碎,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般扳肛。 火紅的嫁衣襯著肌膚如雪傻挂。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天挖息,我揣著相機(jī)與錄音金拒,去河邊找鬼。 笑死套腹,一個胖子當(dāng)著我的面吹牛绪抛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播电禀,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼幢码,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了鞭呕?” 一聲冷哼從身側(cè)響起蛤育,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎葫松,沒想到半個月后瓦糕,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡腋么,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年咕娄,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片珊擂。...
    茶點(diǎn)故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡圣勒,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出摧扇,到底是詐尸還是另有隱情圣贸,我是刑警寧澤,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布扛稽,位于F島的核電站吁峻,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏在张。R本人自食惡果不足惜用含,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望帮匾。 院中可真熱鬧啄骇,春花似錦、人聲如沸瘟斜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至虽惭,卻和暖如春槽华,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背趟妥。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工猫态, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人披摄。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓亲雪,卻偏偏與公主長得像,于是被迫代替她去往敵國和親疚膊。 傳聞我的和親對象是個殘疾皇子义辕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評論 2 355

推薦閱讀更多精彩內(nèi)容