引言
說起
redis
的資料結構,大家可能對五大基礎資料類型比較熟悉:
String
,
Hash
List
Set
Sorted Set
。那麼除此之外,還有三大衍生資料結構,大家平時是很少接觸的,即:
bitmaps
、
hyperloglog
geo
另外,我覺得,這三個資料結構,隻能說是錦上添花。真正在項目中,我還真沒用過。
下面大家來看看這三大資料結構的定義和用途
bitmaps
定義
說到這個
bitmaps
,其實它就是
String
,但它可以對
String
的位進行操作。然後呢,這個位操作,有自己的指令,是以和操作
String
的
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
指令取出,取出每一位的内容。
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登陸了系統!
網上有一張和傳統集合結構的占用空間對比圖,貼出來,給大家看看

注意了,再強調一次,使用此結構是存在誤差的!比如你
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)
距離
100km
和
200km
範圍内的坐标有哪些
redis> GEORADIUS Sicily 15 37 100 km 1) "Catania" redis> GEORADIUS Sicily 15 37 200 km 1) "Palermo" 2) "Catania"
總結,我目前還沒涉及到和地圖有關的業務,是以該結構用的還比較少。大家根據項目具體需求使用即可!
作者:孤獨煙
出處: http://rjzheng.cnblogs.com/
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。如果覺得還有幫助的話,可以點一下右下角的【推薦】。