場(chǎng)景:
關(guān)系服務(wù),負(fù)責(zé)維護(hù)用戶的關(guān)注關(guān)系,粉絲關(guān)系,如微博中的關(guān)注關(guān)系淌山,其中使用redis HASH結(jié)構(gòu)維護(hù)用戶的粉絲關(guān)系
結(jié)構(gòu)如圖下:
問(wèn)題:
對(duì)于業(yè)務(wù)場(chǎng)景需要查詢A,B,C,D是否為E的粉絲,HASH結(jié)構(gòu)效率最高」苏埃現(xiàn)實(shí)中遇到的問(wèn)題是泼疑,對(duì)于大V用戶有千萬(wàn)粉絲時(shí),緩存失效后load操作
時(shí)間太長(zhǎng)荷荤,通過(guò)后臺(tái)日志退渗,一個(gè)20w粉絲的主播,loadDB數(shù)據(jù)至緩存需要1s以上蕴纳,要知道会油,這期間,redis可是不響應(yīng)任何其他請(qǐng)求的袱蚓。
優(yōu)化:
redis層優(yōu)化:
首先先瀏覽了一下redis的源碼钞啸,解壓包后几蜻,查看src目錄下的t_hash.c
hmset操作喇潘,則是我調(diào)用的方法,查看可知梭稚,內(nèi)部是循環(huán)調(diào)用hashTypeSet颖低,將應(yīng)用端傳進(jìn)去的批量key-value逐個(gè)添加,
除此之外弧烤,系統(tǒng)會(huì)對(duì)目前hash結(jié)構(gòu)引用的type類型進(jìn)行判定忱屑,來(lái)選擇合適的編碼規(guī)則,redis hash有兩種編碼暇昂,ziplist,
hash_table,當(dāng)存入key-value字節(jié)長(zhǎng)度少于64莺戒,并且hash內(nèi)size少于512時(shí),redis會(huì)使用ziplist存儲(chǔ)急波,反之會(huì)使用hash_table
做存儲(chǔ)从铲。
其中
hashTypeTryConversion方法校驗(yàn)key-value是否過(guò)長(zhǎng),
hashTypeSet插入key-value時(shí)澄暮,會(huì)檢測(cè)容器容量名段,當(dāng)容量大于hash_max_ziplist_entries時(shí)阱扬,
會(huì)做類型轉(zhuǎn)換操作。默認(rèn)的配置為:
hash容量擴(kuò)充伸辟,與rehash麻惶,由代碼可知,當(dāng)初始容量為0時(shí)信夫,會(huì)取系統(tǒng)配置
DICT_HT_INITIAL_SIZE做初始化窃蹋,當(dāng)容量滿了時(shí),會(huì)取當(dāng)前已用容量2倍來(lái)做初始化静稻。
dictExpand做擴(kuò)容操作脐彩,dict結(jié)構(gòu)體維護(hù)著兩個(gè)table 一個(gè)代表擴(kuò)容前,一個(gè)代表擴(kuò)容后姊扔,擴(kuò)容時(shí)
擴(kuò)容后的table則為ht[1].
筆者通過(guò)修改hash擴(kuò)容時(shí)的配置惠奸,使得不是使得當(dāng)前容量2倍進(jìn)行擴(kuò)容,而是直接每次擴(kuò)容增加20w恰梢,這樣的話佛南,筆者的30w粉絲數(shù),大約進(jìn)行兩次擴(kuò)容即可嵌言。比之前由4開(kāi)始的初始容量進(jìn)行擴(kuò)容會(huì)減少7次左右的擴(kuò)容操作嗅回。
不過(guò)還是無(wú)法解決問(wèn)題,redis基于內(nèi)存級(jí)的擴(kuò)容沒(méi)有我們想象中那么慢摧茴,至少修改后绵载,對(duì)于30w的量只有數(shù)十毫秒級(jí)的優(yōu)化,對(duì)于秒級(jí)的時(shí)間總量苛白,這點(diǎn)優(yōu)化遠(yuǎn)遠(yuǎn)不夠娃豹。所以筆者放棄了此種方法。