Redis沒聽過這些數據結構你就out了

面試官: 我看你簡歷上說在你的項目中使用了 Redis,並使用它作了緩存,你能給我介紹一下 Redis 的五種基本數據類型嗎?git

面試者內心想:昨天看了 FrancisQRedis基礎文章 ,我還怕你嗎? 這麼簡單難不倒個人!github

因而說道:emmm,Redis 中有 string字符串,hash哈希,list列表,set無序集合,zset有序集合,這五種數據類型。面試

面試官:除了這五種基本數據類型你還了解過其餘 Redis 提供的額外的數據類型嗎?你說你用 Redis 作了緩存,好比我如今查詢用戶用一個原本就不會存在的 ID 去調你接口,這樣的 緩存穿透 如何防範呢?算法

面試者內心想:昨天看了 FrancisQRedis基礎文章,貌似在開頭說了什麼bitMap? 什麼h??Log? GeF?? 啥玩意想不起來了,什麼緩存穿透?啥玩意??這 FrancisQ 明顯坑個人,就給我講了五種基礎數據類型,害我如今啥都不會數據庫

沒辦法了硬着頭皮往上懟:emm, 有了解過 bitMap,緩存穿透我沒接觸過。網頁爬蟲

面試官:那你有使用過 bitMap 實現什麼功能麼?數組

面試者內心想:完了完了,這場涼了,都怪 FrancisQ ,回去找他算帳。緩存

內心已經涼涼:沒有。。。數據結構

寫在前面的話

其實 FrancisQ 只是一個沒有參加面試過的小白,在讀大三,想明年暑期實習,因此在學習之餘會寫一些文章進行分享並自我總結(不爲賺錢),若是以爲 FrancisQ 寫的還不錯的話,給我點個贊哦 (#^.^#),其實我只是想早日到 LV4。固然我還有分享其餘文章好比 SSM框架的原理解析和實現MySQL 等等,若是感興趣的也能夠關注我。併發

固然各位大佬有實習崗位的能夠幫幫我哈,哈哈哈。

多餘的話很少說,今天給你們帶來的是 Redis 中的四種特殊的數據結構 bitmaphyperLogLogbloomFilterGeoHash 。這四種數據結構其實有點相似於算法層面了,好比 GeoHash 其實就是一個 zsetbitmap 就是 string,只是使用的方法不一樣致使了更多的功能。

BloomFilter

介紹以及場景使用

BloomFilter 不熟悉的話,對下面的圖片你們確定很熟悉吧?別告訴我你只玩過 王者農藥

BloomFilter 中文名就是 布隆過濾器,做爲過濾器,有沒有感受很像 LOL 中布隆的 E技能(堅如盤石) ?

布隆過濾器是一個叫 布隆 的人提出來的,它是經過一個 大型位數組和幾個不一樣的hash函數 來實現的,咱們能夠把布隆過濾器理解爲一個 不精確的set 。咱們都知道 set 能夠去重,使用 set 能夠幫咱們判斷集合中是否已經存在某些元素而且或者幫咱們實現去重功能。

可是,set 提供精確的去重功能的同時也給咱們帶來了一個更大的問題——空間消耗

好比這個時候咱們進行網頁爬蟲,須要對爬過的 url 進行去重以免爬到已經爬過的網站,若是咱們使用 set 那麼也就意味着咱們須要將全部爬過的 url 放入集合中,假設一個 url 64字節,那麼一億個 url 意味着咱們須要佔用 6GB,十億就是 60GB 左右。

請注意,是內存。

好比這個時候咱們要進行垃圾郵件或者垃圾短信的過濾,咱們須要從數十億個垃圾郵件列表或者垃圾電話列表中進行判斷此時的郵件或者短信是不是垃圾的。若是咱們此時使用 set 那麼佔用空間不用我多說了,也是 百GB級別 的。

上面的面試中我提到了 緩存穿透用戶故意請求數據庫原本就不存在的(好比ID = -1),這個時候若是不作處理那麼確定會穿透緩存去查詢數據庫,一個查詢還好,若是幾千,幾萬個同時進來呢?你的數據庫頂得住嗎?那麼此時咱們使用 set 進行處理,佔用那麼多內存空間,你以爲值得嗎???或者說,還有沒有更好的方法了?

上面所講的三個典型場景,網站去重,垃圾郵件過濾,緩存穿透 ,這三個只要使用 BloomFilter 就能完美解決。

你有沒有發現,上面三個場景其實對精度要求都不是很高,尤爲是垃圾郵件過濾,其實偶爾收到幾個垃圾郵件也無所謂的。像緩存穿透,也正好符合了 BloomFilter 的一個特性 他說有的不必定有,他說沒有的確定沒有,我說你這個 ID 在數據庫不存在那就真的不存在,老子把你過濾了就是這麼自信,怎麼,你打我???

原理探究

聊了這麼久的概念和應用場景,是否是還對 BloomFilter 怎麼能進行去重的仍是一臉懵逼? 下面咱們就聊一聊 BloomFilter 的實現原理。首先給你們放一張結構圖。

其中 F、G、H 是幾種 無偏 Hash 函數,底下是一個 大型的位數組,當咱們向 BloomFilter 添加數據的時候,它首先會將咱們的數據(key)作幾回hash運算(這裏就是FGH),每一個hash運算都會獲得一個不用的位數組索引下標,此時咱們就將算出的幾個下標的位置的值改爲1就行。若是判斷元素是否存在,只要 判斷所在的全部索引下標的值都是1 就好了。

其實你也發現了,在 BloomFilter 中會出現不一樣key所算出的下標重複了,如上圖所示,這就是偏差的來源( 你能夠配置初始大小和錯誤率來控制偏差 )也是他說有的不必定有,他說沒有的確定沒有 這一特性的根本緣由,由於若是全是0或者存在0那麼確定不存在,若是全是1也有多是別的幾個key給放進去的1

基本使用

由於 BloomFilterRedis 的擴展模塊,因此須要額外下載,你可使用 Docker 進行拉取。安裝步驟我不作詳細解釋,你能夠到它的github上學習怎麼安裝 github.com/RedisBloom/…

安裝完以後咱們就能夠愉快的使用啦。

  • bf.add key element 添加
  • bf.exists key element 判斷是否存在
  • bf.madd key element1 element2 ... 批量添加
  • bf.mexists key element1 element2 ... 批量判斷

命令很簡單,你能夠本身去嘗試。

HyperLogLog

介紹以及場景使用

Redis 中還有一個會存在偏差的數據結構 HyperLogLog

咱們首先思考一個場景,當老闆讓咱們計算頁面的 UV 咱們該怎麼辦?

若是訪問量不大使用 set 進行用戶去重徹底能夠,可是訪問量若是有幾百萬,幾千萬,那麼就會又遇到上面提到的 浪費空間 的問題。若是咱們這個時候有一個能 進行去重且能進行計數的數據結構就行了。

這個時候 HyperLogLog 就閃亮登場了!它能提供不精確的去重計數方案(偏差值在 0.81% 左右),不精確就不精確哇,UV 要你多精確?0.81%咱們也能接受。最重要的是 HyperLogLog 只佔用 12KB 的內存。

使用方法和場景實踐

  • pfadd key element 添加
  • pfcount key 計算
  • pfmerge destkey sourcekey1 sourcekey2 ... 合併

命令都是 pf 開頭是由於這是一個名叫 Philippe Flajolet 的教授發明的。

能夠看到就這三個基本命令,很簡單很容易掌握。那咱們來動手實踐一下吧。

BitMap

介紹和使用場景

首先咱們再來思考一個比較有意思的場景,老闆想讓你統計一年內多個用戶之間他們同時在線的天數,這個時候你怎麼辦?

你可能會想到使用 hash 存儲,這太浪費空間了,有沒有更好的辦法呢?答案是有的,Redis 中使用了 bitmap位圖。

咱們知道,字符串中一個字符是使用8個比特來表示的(如上圖),在 Redisbitmap 底層就是 string,也能夠說 string 底層就是 bitmap

若是有了這個咱們是否是能夠用來計算一個用戶在指定時間內簽到的次數?也就是一個位置表明一天,0表明未簽到,1表明簽到,在上圖中,該用戶在八天內簽到了四次。

Redis 中的 bitmap 還提供了多個 bitmap 進行與,或,異或運算的命令,固然還有單個 bitmap 的 非 運算。這是否是給你提供了一點思路對於咱們一開始的需求呢?

基本命令使用

  • setbit key index 0/1 設置某位的值
  • getbit key index 獲取某位的值
  • bitcount key start end 獲取指定範圍內爲1的數量

須要注意的是,這裏的start 和 end是指的字符位置不是比特位置!!!包括下面的 bitpos 也是

  • bitpos key bit start end 獲取第一個值爲bit的從start到end字符索引範圍的位置
  • bitop and/or/xor/not destkey key1 key2 對多個 bitmap 進行邏輯運算。

對於bitmap還有一個好玩的指令就是 bitfield ,這裏我不作過多介紹,感興趣的同窗本身能夠了解一下。

動手實踐

咱們首先來實現一下統計用戶簽到次數的功能。

還記得咱們一開始的問題嗎?統計一年內多個用戶之間他們同時在線的天數,咱們有了 bitmap 還怕什麼。

GeoHash

介紹和場景運用

GeoHash 經常使用來計算 附近的人,附近的商店

試想一下若是咱們使用 關係數據庫 來存儲某個元素的地址 (id,經度,緯度) 。這個時候咱們該如何計算附近的人?難道咱們要遍歷全部元素位置並作距離計算?這顯然不可能。

固然你可使用劃分區域並使用 SQL 語句圈出區域,而後創建 雙向複合索引 來提高性能,可是數據庫的併發能力畢竟有限,咱們能不能使用 Redis 來作呢?

答案是能夠的,Redis 中使用了 GeoHash 提供了很好的解決方案。具體原理是將地球當作一個平面,並把二維座標映射成一維(精度損失的緣由)。若是對其中的算法感興趣你能夠本身額外去了解,篇幅有限不作過多說明。

基本命令和使用實戰

  • geoadd key longitude latitude element(後面可配置多個三元組) 添加元素
  • geodist key element1 element2 unit 計算兩個元素的距離
  • geopos key element [element] 獲取元素的位置
  • geohash key element 獲取元素hash
  • georadiusbymember key element distanceValue unit count countValue ASC/DESC [withdist] [withhash] [withcoord] 獲取元素附近的元素 可附加後面選項[距離][hash][座標]
  • georadius key longitude latitude distanceValue unit count countValue ASC/DESC [withdist] [withhash] [withcoord] 和上面同樣只是元素改爲了指定座標值

總結

這篇文章中我想你們介紹了 Redis 另外的四種特殊數據結構,他們分別是 BloomFilter,HyperLogLog,BitMap還有GeoHash。而且我還想大家介紹瞭如何使用他們,他們的運用場景有哪些,但願對大家有幫助。

很是感謝你能看到這裏,若是喜歡或者對你有幫助別忘了點贊哦。你也能夠關注我,我會常常作些學習分享給你們。

下面是這篇文章的知識圖譜總結,喜歡就給我點贊哦,不要白嫖我┭┮﹏┭┮。

相關文章
相關標籤/搜索