【轉】高可用Redis(六):瑞士軍刀之bitmap,HyperLoglog和GEO

1.bitmap位圖

1.1 bitmap位圖的概念

首先來看一個例子,字符串big,css

字母b的ASCII碼爲98,轉換成二進制爲 01100010
字母i的ASCII碼爲105,轉換成二進制爲 01101001
字母g的ASCII碼爲103,轉換成二進制爲 01100111

若是在Redis中,設置一個key,其值爲big,此時能夠get到big這個值,也能夠獲取到 big的ASCII碼每個位對應的值,也就是0或1python

例如:nginx

127.0.0.1:6379> set hello big OK 127.0.0.1:6379> getbit hello 0 # b的二進制形式的第1位,即爲0 (integer) 0 127.0.0.1:6379> getbit hello 1 # b的二進制形式的第2位,即爲1 (integer) 1

big長度爲3個字節,對應的長度爲24位,使用getbit命令能夠獲取到big對應的位的對應的值git

因此Redis是能夠直接對位進行操做的redis

1.2 bitmap的經常使用命令

1.2.1 setbit命令

setbit key offset vlaue 給位圖指定索引設置值

例子:算法

127.0.0.1:6379> set hello big # 設置鍵值對,key爲'hello',value爲'big' OK 127.0.0.1:6379> setbit hello 7 1 # 把hello二進制形式的第8位設置爲1,以前的ASCII碼爲98,如今改成99,即把b改成c (integer) 0 # 返回的是以前這個位上的值 127.0.0.1:6379> get hello # 修改以後,獲取'hello'的值,爲'cig' "cig"

上面big的長度只有24位,若是使用setbit命令時,指定的位大於目標的長度時sql

127.0.0.1:6379> setbit hello 50 1 (integer) 0 127.0.0.1:6379> get hello "cig\x00\x00\x00 "

從第25開始到第49位,中間用0來填充,第50位纔會被設置爲1ruby

1.2.2 getbit命令

getbit key offset 獲取位圖指定索引的值

例子:微信

127.0.0.1:6379> getbit hello 25 (integer) 0 127.0.0.1:6379> getbit hello 49 (integer) 0 127.0.0.1:6379> getbit hello 50 (integer) 1

1.2.3 bitcount命令

bitcount key [start end] 獲取位圖指定範圍(start到end,單位爲字節,若是不指定就是獲取所有)位值爲1的個數

例子:markdown

127.0.0.1:6379> bitcount hello (integer) 14 127.0.0.1:6379> bitcount hello 0 23 (integer) 14

1.2.4 bitop命令

bitop op dtstkey key [key...] 作多個bitmap的and(交集),or(並集),not(非),xor(異或)操做並將結果保存在destkey中 bitpos key targetBit [start] [end] 計算位圖指定範圍(start到end,單位爲字節,若是不指定就是獲取所有)第一個偏移量對應的值等於targetBit的位置 

1.3 bitmap位圖應用

若是一個網站有1億用戶,假如user_id用的是整型,長度爲32位,天天有5千萬獨立用戶訪問,如何判斷是哪5千萬用戶訪問了網站

1.3.1 方式一:用set來保存

使用set來保存數據運行一天須要佔用的內存爲

32bit * 50000000 = (4 * 50000000) / 1024 /1024 MB,約爲200MB

運行一個月須要佔用的內存爲6G,運行一年佔用的內存爲72G

30 * 200 = 6G

1.3.2 方式二:使用bitmap的方式

若是user_id訪問網站,則在user_id的索引上設置爲1,沒有訪問網站的user_id,其索引設置爲0,此種方式運行一天佔用的內存爲

1 * 100000000 = 100000000 / 1014 /1024/ 8MB,約爲12.5MB

運行一個月佔用的內存爲375MB,一年佔用的內存容量爲4.5G

因而可知,使用bitmap能夠節省大量的內存資源

1.4 bitmap使用經驗

bitmap是string類型,單個值最大可使用的內存容量爲512MB setbit時是設置每一個value的偏移量,能夠有較大耗時 bitmap不是絕對好,用在合適的場景最好

2.HyperLoglog

2.1 HyperLoglog簡介

基於HyperLogLog算法,極小空間完成獨立數量統計

維基百科地址

2.2 經常使用命令

pfadd key element [element...] 向hyperloglog添加元素 pfcount key [key...] 計算hyperloglog的獨立總數 prmerge destkey sourcekey [sourcekey...] 合併多個hyperloglog

例子:

127.0.0.1:6379> pfadd unique_ids1 'uuid_1' 'uuid_2' 'uuid_3' 'uuid_4' # 向unique_ids1中添加4個元素 (integer) 1 127.0.0.1:6379> pfcount unique_ids1 # 查看unique_ids1中元素的個數 (integer) 4 127.0.0.1:6379> pfadd unique_ids1 'uuid_1' 'uuid_2' 'uuid_3' 'uuid_10' # 再次向unique_ids1中添加4個元素 (integer) 1 127.0.0.1:6379> pfcount unique_ids1 # 因爲兩次添加的value有重複,因此unique_ids1中只有5個元素 (integer) 5 127.0.0.1:6379> pfadd unique_ids2 'uuid_1' 'uuid_2' 'uuid_3' 'uuid_4' # 向unique_ids2中添加4個元素 (integer) 1 127.0.0.1:6379> pfcount unique_ids2 # 查看unique_ids2中元素的個數 (integer) 4 127.0.0.1:6379> pfadd unique_ids2 'uuid_4' 'uuid_5' 'uuid_6' 'uuid_7' # 再次向unique_ids2中添加4個元素 (integer) 1 127.0.0.1:6379> pfcount unique_ids2 # 再次查看unique_ids2中元素的個數,因爲兩次添加的元素中有一個重複,因此有7個元素 (integer) 7 127.0.0.1:6379> pfmerge unique_ids1 unique_ids2 # 合併unique_ids1和unique_ids2 OK 127.0.0.1:6379> pfcount unique_ids1 # unique_ids1和unique_ids2中有重複元素,因此合併後的hyperloglog中只有8個元素 (integer) 8

2.3 HyperLoglog內存消耗(百萬獨立用戶)

例子:

127.0.0.1:6379> flushall # 清空Redis中全部的key和value OK 127.0.0.1:6379> info # 查看Redis佔用的內存量 ...省略 # Memory used_memory:833528 used_memory_human:813.99K # 此時Redis中沒有任何鍵值對,佔用814k內存 used_memory_rss:5926912 used_memory_rss_human:5.65M used_memory_peak:924056 used_memory_peak_human:902.40K total_system_memory:1023938560 total_system_memory_human:976.50M used_memory_lua:37888 used_memory_lua_human:37.00K maxmemory:0 maxmemory_human:0B maxmemory_policy:noeviction mem_fragmentation_ratio:7.11 mem_allocator:jemalloc-3.6.0 ...省略

運行以下python代碼:

import redis
import time client = redis.StrictRedis(host='192.168.81.101',port=6379) key = 'unique' start_time = time.time() for i in range(1000000): client.pfadd(key,i)

等待python代碼運行完成,再次查看Redis佔用的內存數

127.0.0.1:6379> info ...省略 # Memory used_memory:849992 used_memory_human:830.07K used_memory_rss:5939200 used_memory_rss_human:5.66M used_memory_peak:924056 used_memory_peak_human:902.40K total_system_memory:1023938560 total_system_memory_human:976.50M used_memory_lua:37888 used_memory_lua_human:37.00K maxmemory:0 maxmemory_human:0B maxmemory_policy:noeviction mem_fragmentation_ratio:6.99 mem_allocator:jemalloc-3.6.0 ...省略

能夠看到,使用hyperloglog向redis中存入100萬條數據,需佔用的內存爲

830.07K - 813.99K約爲16k

佔用的內存不多。

固然天下沒有免費的午飯,hyperloglog也有很是明顯的侷限性

首先,hyperloglog有必定的錯誤率,在使用hyperloglog進行數據統計的過程當中,hyperloglog給出的數據不必定是對的
按照維基百科的說法,使用hyperloglog處理10億條數據,佔用1.5Kb內存時,錯誤率爲2% 其次,無法從hyperloglog中取出單條數據,這很容易理解,使用16KB的內存保存100萬條數據,此時還想把100萬條數據取出來,顯然是不可能的

2.4 HyperLoglog注意事項

使用hyperloglog進行數據統計時,須要考慮三個因素:

1.是否須要不多的內存去解決問題,
2.是否能容忍錯誤
3.是否須要單條數據

3.GEO

3.1 GEO簡介

GEO即地址信息定位
能夠用來存儲經緯度,計算兩地距離,範圍計算等

如上圖中,計算北京到天津兩地之間的距離

3.2 GEO經常使用命令

3.2.1 geoadd命令

geoadd key longitude latitude member [longitude latitude member...] 增長地理位置信息

如上圖是5個城市經緯度相關數據

127.0.0.1:6379> geoadd cities:locations 116.28 39.55 beijing # 添加北京的經緯度 (integer) 1 127.0.0.1:6379> geoadd cities:locations 117.12 39.08 tianjin 114.29 38.02 shijiazhuang # 添加天津和石家莊的經緯度 (integer) 2 127.0.0.1:6379> geoadd cities:locations 118.01 39.38 tangshan 115.29 38.51 baoding # 添加唐山和保定的經緯度 (integer) 2

3.2.2 geppos命令

geopos key member [member...] 獲取地理位置信息

例子:

127.0.0.1:6379> geopos cities:locations tianjin # 獲取天津的地址位置信息 1) 1) "117.12000042200088501" 2) "39.0800000535766543"

3.2.3 geodist命令

geodist key member1 member2 [unit] 獲取兩個地理位置的距離,unit:m(米),km(公里),mi(英里),ft(尺)

例子:

127.0.0.1:6379> geodist cities:locations tianjin beijing km "89.2061" 127.0.0.1:6379> geodist cities:locations tianjin baoding km "170.8360"

3.2.4 georadius命令和georadiusbymember命令

georedius key longitude latitude radiusm|km|ft|mi [withcoord] [withdist] [withhash] [COUNT count] [asc|desc] [store key][storedist key] georadiusbymember key member radiusm|km|ft|mi [withcoord] [withdist] [withhash] [COUNT count] [asc|desc] [store key][storedist key] 獲取指定位置範圍內的地理位置信息集合  withcoord:返回結果中包含經緯度  withdist:返回結果中包含距離中心節點位置  withhash:返回結果中包含geohash  COUNT count:指定返回結果的數量  asc|desc:返回結果按照距離中心節點的距離作升序或者降序  store key:將返回結果的地理位置信息保存到指定鍵  storedist key:將返回結果距離中心節點的距離保存到指定鍵

例子:

127.0.0.1:6379> georadiusbymember cities:locations beijing 150 km # 獲取距離北京150km範圍內的城市 1) "beijing" 2) "tianjin" 3) "tangshan" 4) "baoding"

3.3 GEO相關說明

Redis的GEO功能是從3.2版本添加 geo功能基於zset實現 geo沒有刪除命令

3.3.1 使用zrem命令來進行geo的刪除操做

命令:

zrem key member

例子:

127.0.0.1:6379> georadiusbymember cities:locations beijing 150 km 1) "beijing" 2) "tianjin" 3) "tangshan" 4) "baoding" 127.0.0.1:6379> zrem cities:locations baoding (integer) 1 127.0.0.1:6379> georadiusbymember cities:locations beijing 150 km 1) "beijing" 2) "tianjin" 3) "tangshan"

3.4 GEO的應用場景

微信搖一搖
相關文章
相關標籤/搜索