溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Redis大數(shù)據(jù)量Key存儲需求及解決方案

發(fā)布時間:2021-06-24 10:45:10 來源:億速云 閱讀:184 作者:chen 欄目:編程語言

這篇文章主要介紹“Redis大數(shù)據(jù)量Key存儲需求及解決方案”,在日常操作中,相信很多人在Redis大數(shù)據(jù)量Key存儲需求及解決方案問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Redis大數(shù)據(jù)量Key存儲需求及解決方案”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!

需求背景

該應(yīng)用場景為DMP緩存存儲需求,DMP需要管理非常多的第三方id數(shù)據(jù),其中包括各媒體cookie與自身cookie(以下統(tǒng)稱supperid)的mapping關(guān)系,還包括了supperid的人口標(biāo)簽、移動端id(主要是idfa和imei)的人口標(biāo)簽,以及一些黑名單id、ip等數(shù)據(jù)。

在hdfs的幫助下離線存儲千億記錄并不困難,然而DMP還需要提供毫秒級的實(shí)時查詢。由于cookie這種id本身具有不穩(wěn)定性,所以很多的真實(shí)用戶的瀏覽行為會導(dǎo)致大量的新cookie生成,只有及時同步mapping的數(shù)據(jù)才能命中DMP的人口標(biāo)簽,無法通過預(yù)熱來獲取較高的命中,這就跟緩存存儲帶來了極大的挑戰(zhàn)。

經(jīng)過實(shí)際測試,對于上述數(shù)據(jù),常規(guī)存儲超過五十億的kv記錄就需要1T多的內(nèi)存,如果需要做高可用多副本那帶來的消耗是巨大的,另外kv的長短不齊也會帶來很多內(nèi)存碎片,這就需要超大規(guī)模的存儲方案來解決上述問題。

存儲何種數(shù)據(jù)

人?標(biāo)簽主要是cookie、imei、idfa以及其對應(yīng)的gender(性別)、age(年齡段)、geo(地域)等;mapping關(guān)系主要是媒體cookie對supperid的映射。以下是數(shù)據(jù)存儲?示例:

PC端的ID:媒體編號-媒體cookie=>supperid

supperid => { age=>年齡段編碼,gender=>性別編碼,geo=>地理位置編碼 }

Device端的ID:

imei or idfa => { age=>年齡段編碼,gender=>性別編碼,geo=>地理位置編碼 }

顯然PC數(shù)據(jù)需要存儲兩種key=>value還有key=>hashmap,?而Device數(shù)據(jù)需要存儲?一種key=>hashmap即可。

數(shù)據(jù)特點(diǎn)

短key短value:

其中superid為21位數(shù)字:比如1605242015141689522
imei為小寫md5:比如2d131005dc0f37d362a5d97094103633
idfa為大寫帶”-”md5:比如:51DFFC83-9541-4411-FA4F-356927E39D04

媒體自身的cookie長短不一;
需要為全量數(shù)據(jù)提供服務(wù),supperid是百億級、媒體映射是千億級、移動id是幾十億級;
每天有十億級別的mapping關(guān)系產(chǎn)生;
對于較大時間窗口內(nèi)可以預(yù)判熱數(shù)據(jù)(有一些存留的穩(wěn)定cookie);
對于當(dāng)前mapping數(shù)據(jù)無法預(yù)判熱數(shù)據(jù),有很多是新生成的cookie;

存在的技術(shù)挑戰(zhàn)

1)長短不一容易造成內(nèi)存碎片;
2)由于指針大量存在,內(nèi)存膨脹率比較高,一般在7倍,純內(nèi)存存儲通??;
3)雖然可以通過cookie的行為預(yù)判其熱度,但每天新生成的id依然很多(百分比比較敏感,暫不透露);
4)由于服務(wù)要求在公網(wǎng)環(huán)境(國內(nèi)公網(wǎng)延遲60ms以下)下100ms以內(nèi),所以原則上當(dāng)天新更新的mapping和人口標(biāo)簽需要全部in memory,而不會讓請求落到后端的冷數(shù)據(jù);
5)業(yè)務(wù)方面,所有數(shù)據(jù)原則上至少保留35天甚至更久;
6)內(nèi)存至今也比較昂貴,百億級Key乃至千億級存儲方案勢在必行!

解決方案

5.1 淘汰策略

存儲吃緊的一個重要原因在于每天會有很多新數(shù)據(jù)入庫,所以及時清理數(shù)據(jù)尤為重要。主要方法就是發(fā)現(xiàn)和保留熱數(shù)據(jù)淘汰冷數(shù)據(jù)。網(wǎng)民的量級遠(yuǎn)遠(yuǎn)達(dá)不到幾十億的規(guī)模,id有一定的生命周期,會不斷的變化。所以很大程度上我們存儲的id實(shí)際上是無效的。而查詢其實(shí)前端的邏輯就是廣告曝光,跟人的行為有關(guān),所以一個id在某個時間窗口的(可能是一個campaign,半個月、幾個月)訪問行為上會有一定的重復(fù)性。數(shù)據(jù)初始化之前,我們先利用hbase將日志的id聚合去重,劃定TTL的范圍,一般是35天,這樣可以砍掉近35天未出現(xiàn)的id。另外在Redis中設(shè)置過期時間是35天,當(dāng)有訪問并命中時,對key進(jìn)行續(xù)命,延長過期時間,未在35天出現(xiàn)的自然淘汰。這樣可以針對穩(wěn)定cookie或id有效,實(shí)際證明,續(xù)命的方法對idfa和imei比較實(shí)用,長期積累可達(dá)到非常理想的命中。

5.2 減少膨脹

Hash表空間大小和Key的個數(shù)決定了沖突率(或者用負(fù)載因子衡量),再合理的范圍內(nèi),key越多自然hash表空間越大,消耗的內(nèi)存自然也會很大。再加上大量指針本身是長整型,所以內(nèi)存存儲的膨脹十分可觀。先來談?wù)勅绾伟裬ey的個數(shù)減少。

大家先來了解一種存儲結(jié)構(gòu)。我們期望將key1=>value1存儲在redis中,那么可以按照如下過程去存儲。先用固定長度的隨機(jī)散列md5(key)值作為redis的key,我們稱之為BucketId,而將key1=>value1存儲在hashmap結(jié)構(gòu)中,這樣在查詢的時候就可以讓client按照上面的過程計算出散列,從而查詢到value1。

過程變化簡單描述為:get(key1) -> hget(md5(key1), key1) 從而得到value1。如果我們通過預(yù)先計算,讓很多key可以在BucketId空間里碰撞,那么可以認(rèn)為一個BucketId下面掛了多個key。比如平均每個BucketId下面掛10個key,那么理論上我們將會減少超過90%的redis key的個數(shù)。

具體實(shí)現(xiàn)起來有一些麻煩,而且用這個方法之前你要想好容量規(guī)模。我們通常使用的md5是32位的hexString(16進(jìn)制字符),它的空間是128bit,這個量級太大了,我們需要存儲的是百億級,大約是33bit(2的33次方),所以我們需要有一種機(jī)制計算出合適位數(shù)的散列,而且為了節(jié)約內(nèi)存,我們需要利用全部字符類型(ASCII碼在0~127之間)來填充,而不用HexString,這樣Key的長度可以縮短到一半。

下面是具體的實(shí)現(xiàn)方式

public static byte [] getBucketId(byte [] key, Integer bit) {

    MessageDigest mdInst = MessageDigest.getInstance("MD5");

    mdInst.update(key);byte [] md = mdInst.digest();byte [] r = new byte[(bit-1)/7 + 1];// 因?yàn)橐粋€字節(jié)中只有7位能夠表示成單字符,ascii碼是7位int a = (int) Math.pow(2, bit%7)-2;

    md[r.length-1] = (byte) (md[r.length-1] & a);

    System.arraycopy(md, 0, r, 0, r.length);for(int i=0;i<r.length;i++) {if(r[i]<0) r[i] &= 127;
    }return r;
}

參數(shù)bit決定了最終BucketId空間的大小,空間大小集合是2的整數(shù)冪次的離散值。這里解釋一下為何一個字節(jié)中只有7位可用,是因?yàn)閞edis存儲key時需要是ASCII(0~127),而不是byte array。如果規(guī)劃百億級存儲,計劃每個桶分擔(dān)10個kv,那么我們只需2^30=1073741824的桶個數(shù)即可,也就是最終key的個數(shù)。

5.3 減少碎片

碎片主要原因在于內(nèi)存無法對齊、過期刪除后,內(nèi)存無法重新分配。通過上文描述的方式,我們可以將人口標(biāo)簽和mapping數(shù)據(jù)按照上面的方式去存儲,這樣的好處就是redis key是等長的。

另外對于hashmap中的key我們也做了相關(guān)優(yōu)化,截取cookie或者deviceid的后六位作為key,這樣也可以保證內(nèi)存對齊,理論上會有沖突的可能性,但在同一個桶內(nèi)后綴相同的概率極低(試想id幾乎是隨機(jī)的字符串,隨意10個由較長字符組成的id后綴相同的概率*桶樣本數(shù)=發(fā)生沖突的期望值<<0.05,也就是說出現(xiàn)一個沖突樣本則是極小概率事件,而且這個概率可以通過調(diào)整后綴保留長度控制期望值)。而value只存儲age、gender、geo的編碼,用三個字節(jié)去存儲。

另外提一下,減少碎片還有個很low但是有效的方法,將slave重啟,然后強(qiáng)制的failover切換主從,這樣相當(dāng)于給master整理的內(nèi)存的碎片。

推薦Google-tcmalloc, facebook-jemalloc內(nèi)存分配,可以在value不大時減少內(nèi)存碎片和內(nèi)存消耗。有人測過大value情況下反而libc更節(jié)約。

到此,關(guān)于“Redis大數(shù)據(jù)量Key存儲需求及解決方案”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識,請繼續(xù)關(guān)注億速云網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬?shí)用的文章!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI