提及redis
的數據結構,你們可能對五大基礎數據類型比較熟悉:String
,Hash
,List
,Set
,Sorted Set
。那麼除此以外,還有三大衍生數據結構,你們平時是不多接觸的,即:bitmaps
、hyperloglog
、geo
另外,我以爲,這三個數據結構,只能說是錦上添花。真正在項目中,我還真沒用過。 下面你們來看看這三大數據結構的定義和用途java
說到這個bitmaps
,其實它就是String
,但它能夠對String
的位進行操做。而後呢,這個位操做,有本身的命令,因此和操做String
的redis
命令又不大同樣! 能夠這麼理解redis
bitmaps爲一個以位爲單位的數組,數組的每一個單元只能存儲0和1算法
下面舉個例子,好比咱們要作一個set操做,key
爲w
,value
爲h
,那麼執行以下命令數組
127.0.0.1:6379> set w h
OK
127.0.0.1:6379> get w
"h"
複製代碼
那麼h
的ASCII爲0110 1000
接下來,你能夠用位命令getbit
命令取出,取出每一位的內容。bash
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碰撞,致使偏移量一致? 因此,你們瞭解便可。ui
HyperLogLog
並非一種數據結構,而是一種算法,能夠利用極小的內存空間完成獨立總數的統計。 其實,你們可能對該算法比較陌生。咱們java
中有一個庫叫stream-lib
,其中也實現了HyperLogLog
算法。我大概說一下該算法的原理,我不想去長篇大論的搬出數學論文來,你們看着也無聊,這裏Hyper
指的是超級的意思,它的前世是LogLog算法。這裏我走馬觀花的裝13一下,你們能領悟到精髓便可。 假設有以下對話spa
我:"小曲啊,假設啊,我一輪丟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登錄了系統! 網上有一張和傳統集合結構的佔用空間對比圖,貼出來,給你們看看
注意了,再強調一次,使用此結構是存在偏差的!好比你pfadd
了一百萬條數據進去,結果pfcount
的結果可能就999756條!
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)
距離100km
和200km
範圍內的座標有哪些
redis> GEORADIUS Sicily 15 37 100 km
1) "Catania"
redis> GEORADIUS Sicily 15 37 200 km
1) "Palermo"
2) "Catania"
複製代碼