【原創】分布式之redis的三大衍生數據結構


引言

說起redis的數據結構,大家可能對五大基礎數據類型比較熟悉:StringHashListSetSorted Set。那么除此之外,還有三大衍生數據結構,大家平時是很少接觸的,即:bitmapshyperlogloggeo
另外,我覺得,這三個數據結構,只能說是錦上添花。真正在項目中,我還真沒用過。
下面大家來看看這三大數據結構的定義用途

bitmaps

定義

說到這個bitmaps,其實它就是String,但它可以對String的位進行操作。然后呢,這個位操作,有自己的命令,所以和操作Stringredis命令又不大一樣!
可以這么理解

bitmaps為一個以位為單位的數組,數組的每個單元只能存儲0和1

下面舉個例子,比如我們要做一個set操作,keywvalueh,那么執行如下命令

127.0.0.1:6379> set w h
OK
127.0.0.1:6379> get w
"h"

那么h的ASCII為0110 1000
接下來,你可以用位命令getbit命令取出,取出每一位的內容。

127.0.0.1:6379> getbit w 0 #用getbit獲取w第0位的值
(integer) 0
127.0.0.1:6379> getbit w 1 #用getbit獲取w第1位的值
(integer) 1
127.0.0.1:6379> getbit w 2 #用getbit獲取w第2位的值
(integer) 1
127.0.0.1:6379> getbit w 3 #用getbit獲取w第3位的值
(integer) 0

用途

網上傳言,此結構用來統計一定時間內的,活躍的用戶數,使用bitmap的結構比傳統的set結構省空間。然而,這種用途有很大的局限性,我后文會說到。先說一下,網上的說法。
假設有30個用戶,其中有5個用戶,在2018-10-04這天登陸了。假設這5個用戶的userid=2,4,8,11,12。
那么,我們假設key為users:2018-10-04,將其value值用於記錄用戶登陸信息。那么為了記錄上述5個用戶登陸過,我們將該value值的第2位,第4位,第8位,第11位,第12位設為1,即執行下述命令

127.0.0.1:6379> setbit users:2018-10-04 2 1
(integer) 0
127.0.0.1:6379> setbit users:2018-10-04 4 1
(integer) 0
127.0.0.1:6379> setbit users:2018-10-04 8 1
(integer) 0
127.0.0.1:6379> setbit users:2018-10-04 11 1
(integer) 0
127.0.0.1:6379> setbit users:2018-10-04 12 1
(integer) 0

這個時候,比如你要判斷userid=11的用戶,在2018-10-04這天,有沒有登陸過,就執行下述命令

127.0.0.1:6379> getbit users:2018-10-04 11
(integer) 1

結果為1,就代表用戶登陸過。如果返回結果為0,則代表用戶沒登陸過。
如果要統計,2018-10-04,這一天登陸的用戶數,可以執行下面的命令

127.0.0.1:6379> bitcount users:2018-10-04
(integer) 5

上面的命令就可以統計出,2018-10-04,這一天5個用戶登陸過。
ok,到這里大家就查不多能明白了。
先說一下,這里的userid=2,4,8,11,12,可以理解為偏移量。比如實際項目中的userid位1000002,那么偏移量就是2。大家在項目中,可以靈活變通。
然而這種方式有一個局限性。我們在實際項目中,如果userid是使用uuid生成的,那么,你要如何根據這些userid生成偏移量?莫非你還要去找一個hash函數,生成偏移量?就算找到了相應的hash函數,你能確保一定不發生hash碰撞,導致偏移量一致?
所以,大家了解即可。

HyperLogLog

定義

HyperLogLog並不是一種數據結構,而是一種算法,可以利用極小的內存空間完成獨立總數的統計
其實,大家可能對該算法比較陌生。我們java中有一個庫叫stream-lib,其中也實現了HyperLogLog算法。我大概說一下該算法的原理,我不想去長篇大論的搬出數學論文來,大家看着也無聊,這里Hyper指的是超級的意思,它的前世是LogLog算法。這里博主蜻蜓點水的裝13一下,大家能領悟到精髓即可。
假設有如下對話

我:"小曲啊,假設啊,我一輪丟5次硬幣,丟了很多輪之后,發現這幾輪中,最多出現連續的2次反面1次正面,你能猜出來我丟了多少輪么!"
小曲:"應該沒幾輪吧,頂多就七八輪。"
我:"卧槽,這么機智,怎么算的?"
小曲:"很簡單啊,正反面概率都是1/2,連着二次反面,一次正面。不就是1/21/21/2么!"
我:"那要是最多出現連續的4次反面1次正面呢?"
小曲:"那應該是很多很多輪吧!"
我:"果然機智!"

上述聊天,出自我和同事曲之間的,日常互吹!如有雷同,純屬巧合!

好了,原理講完了!只是他的估算算法比較復雜!沒這么簡單而已!而且這么估,誤差還比較大!下面給出算法的偽代碼。

輸入:一個集合
輸出:集合的獨立總數
算法:
     max = 0
     對於集合中的每個元素:
               hashCode = hash(元素)
               num = hashCode二進制表示中最前面連續的0的數量
               if num > max:
                   max = num
     最后的結果是2的(max + 1)次冪

需要說明的是

hashCode = hash(元素)

就是把你的輸入元素,映射成二進制長串。映射成二進制長串后,就可以類比到我最先說的拋硬幣的結果了。至於最后的結果為什么用(max+1),大家可以去查文獻。畢竟這文章是在講redis,不是在講這個算法。而且這個算法,后面還經過了一系列演進,比如將入參集合分為m個部分,然后將m個部分的結果求一個平均數(avg),最后以2的(avg + 1)次冪,來估計獨立總數!這些讀者有興趣可以自行查詢!

用途

這個結構可以非常省內存的去統計各種計數,比如注冊IP數、每日訪問IP數。當然,存在誤差!Redis官方給出的數字是0.81%的失誤率。
用法也很簡單如下所示

127.0.0.1:6379> pfadd ips:2018-10-04 "127.0.0.1" "127.0.0.2" "127.0.0.3" "127.0.0.4" 
(integer) 1
127.0.0.1:6379> pfcount ips:2018-10-04
(integer) 4

上面就是演示了,2018-10-04這天,約4個ip登陸了系統!
網上有一張和傳統集合結構的占用空間對比圖,貼出來,給大家看看
image
注意了,再強調一次,使用此結構是存在誤差的!比如你pfadd了一百萬條數據進去,結果pfcount的結果可能就999756條!

Geo

定義

Geo可以用於存儲經緯度、計算兩地之間的距離、范圍計算等。其底層實現是zset。

用途

主要有以下六組命令

  • geoadd:增加某個地理位置的坐標。
  • geopos:獲取某個地理位置的坐標。
  • geodist:獲取兩個地理位置的距離。
  • georadius:根據給定地理位置坐標獲取指定范圍內的地理位置集合。
  • georadiusbymember:根據給定地理位置獲取指定范圍內的地理位置集合
  • geohash:獲取某個地理位置的geohash值。

我這里直接貼官網文檔的例子,大家有興趣可以自行查詢.
首先,先給key增加兩個坐標

redis> GEOADD Sicily 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania"
(integer) 2

其次,計算兩個坐標之間的舉例

redis> GEODIST Sicily Palermo Catania
"166274.15156960039"

最后,計算距離經緯度(15,37)距離100km200km范圍內的坐標有哪些

redis> GEORADIUS Sicily 15 37 100 km
1) "Catania"

redis> GEORADIUS Sicily 15 37 200 km
1) "Palermo"
2) "Catania"

總結,我目前還沒涉及到和地圖有關的業務,因此該結構用的還比較少。大家根據項目具體需求使用即可!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM