生日悖論: 是指在很多於 23 我的中至少有兩人生日相同的機率大於 50%。例如在一個 30 人的小學班級中,存在兩人生日相同的機率爲 70%。對於 60 人的大班,這種機率要大於 99%。從引發邏輯矛盾的角度來講,生日悖論並非一種 「悖論」。但這個數學事實十分反直覺,故稱之爲一個悖論。 redis
生日悖論是有個有趣的概念,但這和我省上百G的內存有什麼關係?服務器
首先介紹下背景,工做中我負責了一個廣告數據系統,其中一個功能就是對同一次請求的廣告曝光去重,由於咱們只須要知道此次請求這個廣告的一次曝光就好了,那些同一次請求產生的重複曝光記錄下來沒有意義,並且還耗會增長咱們的存儲成本。因此這裏就須要有個邏輯去判斷每條新到的曝光是否只以前已經記錄過的,舊的方案是在redis中存儲請求惟一標識(uuid)和廣告ID(adid),每次數據過來咱們就看redis裏有沒有uuid+adid這個key,有就過濾掉,沒有就不過濾並在redis記錄下來已出現。 app
問題就來了,redis記錄的這份數很大(兩天數據超過400G),並且隨着咱們業務的增加,咱們的Redis集羣快盛不下了…… 固然花錢加機器是最簡單的方式,畢竟能用錢解決的問題都不是問題。而優秀的我,爲了替公司省錢,走了優化的路。分佈式
首先能夠確定的是數據條數不會少,由於業務量就在那裏,因此減小數據量的這條路確定行不通。那是否能夠減小每條數據的長度呢?
咱們再來看下redis存儲的設計,以下圖:
這樣下來一條記錄總共用了45個字節,這個長度能不能縮短? 固然能,咱們能夠截取部分UUID,但這樣又帶來一個新的問題,截取UUID會增長重複的機率,因此首先搞清楚怎麼截取,截多少? 性能
這裏咱們用的是隨機UUID,這個版本中有效二進制位是122個,因此總共有$2^{122}$個有效的UUID。 由於是隨機產生的因此確定有重複的機率,UUID重複的機率有多少? 要算這個重複機率,光有$2^{122}$這個總數還不行,還得知道你擁有的UUID個數。 我把這個問題具體下,求:在$2^{36}$個UUID中有重複的機率是多少?測試
$$ p(n) \approx 1-e^{-\frac{n^{2}}{2 x}} $$優化
這不就是生日悖論的數據放大版嗎? 固然這個機率能夠根據上面公式計算,其中x是UUID的總數$2^{122}$,n是$2^{36}$,引用百度百科的數據,機率爲$4 *10^{-16}$ 這比你出門被隕石撞的機率還小不少。ui
n | 概率 |
---|---|
2^36 | 4 x 10^-16 |
2^41 | 4 x 10^-13 |
2^46 | 4 x 10^-10 |
另外,從上面的公式也能夠看出,在n固定的時候,隨着有效二進制位的減小,機率p就會增長。 回到咱們廣告去重的場景下,天天最大請求數n是基本固定的,並且咱們也能夠忍受一個較小的機率p(小於萬分之一),而後就能夠反推出這個x有多大。 spa
其實只要$\frac{n^{2}}{2 x} < 0.0001$,p就會小於萬分之一。我能夠從歷史數中統計出n的大小,而後計算出x,再留必定的buff,而後根據n的大小從新設計了redis的key。(由於涉及公司數據,這裏不公佈中間計算過程).net
最終有效位我選取了40個有效二進制位(10個16進制位),但我並無直接截取UUID的前10位,由於UUID的前幾位和時間有關,隨機性並不強。我選擇將整個UUID從新md5散列,而後截取md5的前10位,而後拼接adId造成最終的key,以下圖:
明顯看出,key的長度縮小了一半,整體上能節省至少50%的存儲空間。備註:但其實咱們redis的具體存儲實現和上文描述略有差別,爲了避免喧賓奪主上文特地對實際實現作了簡化描述,因此最終實際沒有省一半以上的內存,只省了35%左右。
實際優化就到這了,但其實仍是不夠極致,其實adId中也包含大量的冗餘信息也能夠截取,其實咱們能夠承受更高的重複率,其實咱們能夠把redis數據存儲時間設的更短一些……
上面幾種方法均可以進一步優化,但存儲空間不會有量級級別的減小,而下面一種方式,能夠將存儲空間減少99%以上。
關於布隆過濾器的原理,能夠參考我以前寫的一篇文章布隆過濾器(BloomFilter)原理 實現和性能測試。 布隆過濾器徹底就是爲了去重場景設計的,保守估計咱們廣告去重的場景切到布隆過濾器,至少節省90%的內存。
那爲何我沒有用布隆過濾器,其實仍是由於實現複雜。redis在4.0後支持模塊,其中有人就開發設計了布隆過濾器的模塊RedisBloom,但無奈咱們用的redis 仍是3.x版本 !我也考慮過應用端基於redis去實現布隆過濾器,但咱們應用端是個集羣,須要解決一些分佈式數據一致性的問題,做罷。
其實咱們redis的具體存儲實現和上文描述略有差別,爲了避免喧賓奪主上文特地對實際實現作了簡化描述,因此最終實際沒有省一半以上的內存,只省了35%左右。
最終400G+優化後能減小100多G的內存,其實也就是一臺服務器,即使放到將來也就是少擴容幾臺服務器。對公司而言就是每月節省幾千的成本,我司這種大廠實際上是不會在意這點錢的。不過即使這幾千的成本最終不會轉化成個人工資或者獎金,但像這種優化該作仍是得作。若是每一個人都本着 用最低的成本作一樣事 的原則去作好每一件事,就我司這體量,一個月上千萬的成本仍是能省下來的。
本文來自 https://blog.csdn.net/xindoo