一、“5種基礎”資料型別
Redis 有望所有的key(鍵)都是字串。我們在談基礎資料結構時,討論的是儲存值的資料型別,主要包括常見的5種資料型別,分別是:String、List、Set、Zset、Hash
1。1 Redis資料結構簡介
Redis基礎文章非常多,關於基礎資料結構型別,我推薦你先看下官方網站內容 (opens new window),然後再看下面的小結
首先對redis來說,所有的key(鍵)都是字串。我們在談基礎資料結構時,討論的是儲存值的資料型別,主要包括常見的5種資料型別,分別是:String、List、Set、Zset、Hash。
1。2 基礎資料結構詳解
內容其實比較簡單,我覺得理解的重點在於這個結構怎麼用,能夠用來做什麼?所以我在梳理時,圍繞圖例,命令,執行和場景來闡述
1。2。1 String字串
String是redis中最基本的資料型別,一個key對應一個value。
String型別是二進位制安全的,意思是 redis 的 string 可以包含任何資料。如數字,字串,jpg圖片或者序列化的物件。
圖例
下圖是一個String型別的例項,其中鍵為hello,值為world
命令使用
命令執行
127。0。0。1
:
6379
>
set
hello
world
OK
127。0。0。1
:
6379
>
get
hello
“world”
127。0。0。1
:
6379
>
del
hello
(
integer
)
1
127。0。0。1
:
6379
>
get
hello
(
nil
)
127。0。0。1
:
6379
>
get
counter
“2”
127。0。0。1
:
6379
>
incr
counter
(
integer
)
3
127。0。0。1
:
6379
>
get
counter
“3”
127。0。0。1
:
6379
>
incrby
counter
100
(
integer
)
103
127。0。0。1
:
6379
>
get
counter
“103”
127。0。0。1
:
6379
>
decr
counter
(
integer
)
102
127。0。0。1
:
6379
>
get
counter
“102”
實戰場景
快取
: 經典使用場景,把常用資訊,字串,圖片或者影片等資訊放到redis中,redis作為快取層,mysql做持久化層,降低mysql的讀寫壓力。
計數器
:redis是單執行緒模型,一個命令執行完才會執行下一個,同時資料可以一步落地到其他的資料來源。
session
:常見方案spring session + redis實現session共享
1。2。2 List列表
Redis中的List其實就是連結串列(Redis用雙端連結串列實現List)。
使用List結構,我們可以輕鬆地實現最新訊息排隊功能(比如新浪微博的TimeLine)。List的另一個應用就是訊息佇列,可以利用List的 PUSH 操作,將任務存放在List中,然後工作執行緒再用 POP 操作將任務取出進行執行。
圖例
命令使用
使用列表的技巧
lpush+lpop=Stack(棧)
lpush+rpop=Queue(佇列)
lpush+ltrim=Capped Collection(有限集合)
lpush+brpop=Message Queue(訊息佇列)
命令執行
127。0。0。1
:
6379
>
lpush
mylist
1
2
ll
ls
mem
(
integer
)
5
127。0。0。1
:
6379
>
lrange
mylist
0
-1
1
)
“mem”
2
)
“ls”
3
)
“ll”
4
)
5
)
“1”
127。0。0。1
:
6379
>
lindex
mylist
-1
127。0。0。1
:
6379
>
lindex
mylist
10
#
index
不在
mylist
的區間範圍內
(
nil
)
實戰場景微博
TimeLine
: 有人釋出微博,用lpush加入時間軸,展示新的列表資訊。
訊息佇列
1。2。3 Set集合
Redis 的 Set 是 String 型別的無序集合。集合成員是唯一的,這就意味著集合中不能出現重複的資料。
Redis 中集合是透過雜湊表實現的,所以新增,刪除,查詢的複雜度都是 O(1)。
圖例
命令使用
命令執行
127。0。0。1
:
6379
>
sadd
myset
hao
hao1
xiaohao
hao
(
integer
)
3
127。0。0。1
:
6379
>
smember
myset
1
)
“xiaohao”
2
)
“hao1”
3
)
“hao”
127。0。0。1
:
6379
>
sismember
myset
hao
(
integer
)
1
實戰場景
標籤
(tag),給使用者新增標籤,或者使用者給訊息新增標籤,這樣有同一標籤或者類似標籤的可以給推薦關注的事或者關注的人。
點贊,或點踩,收藏等
,可以放到set中實現
1。2。4 Hash雜湊
Redis hash 是一個 string 型別的 field(欄位) 和 value(值) 的對映表,hash 特別適合用於儲存物件。
圖例
命令使用
命令執行
127。0
。
0
。
1
:
6379
>
hset
user
name1
hao
(
integer
)
1
127。0
。
0
。
1
:
6379
>
hset
user
email1
hao
@
163
。
com
(
integer
)
1
127。0
。
0
。
1
:
6379
>
hgetall
user
1
)
“name1”
2
)
3
)
“email1”
4
)
“hao@163。com”
127。0
。
0
。
1
:
6379
>
hget
user
user
(
nil
)
127。0
。
0
。
1
:
6379
>
hget
user
name1
127。0
。
0
。
1
:
6379
>
hset
user
name2
xiaohao
(
integer
)
1
127。0
。
0
。
1
:
6379
>
hset
user
email2
xiaohao
@
163
。
com
(
integer
)
1
127。0
。
0
。
1
:
6379
>
hgetall
user
1
)
2
)
3
)
4
)
5
)
“name2”
6
)
7
)
“email2”
8
)
“xiaohao@163。com”
實戰場景
快取
: 能直觀,相比string更節省空間,的維護快取資訊,如用
戶
資訊,影片資訊等
1。2。5 Zset有序集合
Redis 有序集合和集合一樣也是 string 型別元素的集合,且不允許重複的成員。不同的是每個元素都會關聯一個 double 型別的分數。redis 正是透過分數來為集合中的成員進行從小到大的排序。
有序集合的成員是唯一的,但分數(score)卻可以重複。集合是透過雜湊表實現的,所以新增,刪除,查詢的複雜度都是 O(1)。
圖例
命令使用
127。0。0。1
:
6379
>
zadd
myscoreset
100
hao
90
xiaohao
(
integer
)
2
127。0。0。1
:
6379
>
ZRANGE
myscoreset
0
-1
1
)
2
)
127。0。0。1
:
6379
>
ZSCORE
myscoreset
hao
“100”
實戰場景
排行榜
:有序集合經典使用場景。例如小說影片等網站需要對使用者上傳的小說影片做排行榜,榜單可以按照使用者關注數,更新時間,字數等打分,做排行
二、3種特殊型別詳解
Redis除了上文中5種基礎資料型別,還有三種特殊的資料型別,分別是 HyperLogLogs(基數統計), Bitmaps (點陣圖) 和 geospatial (地理位置)
2。1 HyperLogLogs(基數統計)
Redis 2。8。9 版本更新了 Hyperloglog 資料結構!
什麼是基數?
舉個例子,A = {1, 2, 3, 4, 5}, B = {3, 5, 6, 7, 9};那麼基數(不重複的元素)= 1, 2, 4, 6, 7, 9; (允許容錯,即可以接受一定誤差)
HyperLogLogs 基數統計用來解決什麼問題
?
這個結構可以非常省記憶體的去統計各種計數,比如註冊 IP 數、每日訪問 IP 數的頁面實時UV、線上使用者數,共同好友數等。
它的優勢體現在哪
?
一個大型的網站,每天 IP 比如有 100 萬,粗算一個 IP 消耗 15 位元組,那麼 100 萬個 IP 就是 15M。而 HyperLogLog 在 Redis 中每個鍵佔用的內容都是 12K,理論儲存近似接近 2^64 個值,不管儲存的內容是什麼,它一個基於基數估算的演算法,只能比較準確的估算出基數,可以使用少量固定的記憶體去儲存並識別集合中的的唯一元素。而且這個估算的基數並不一定準確,是一個帶有 0。81% 標準錯誤的近似值(對於可以接受一定容錯的業務場景,比如IP數統計,UV等,是可以忽略不計的)。
相關命令使用
127。0。0。1
:
6379
>
pfadd
key1
a
b
c
d
e
f
g
h
i
#
建立第一組元素
(
integer
)
1
127。0。0。1
:
6379
>
pfcount
key1
#
統計元素的基數數量
(
integer
)
9
127。0。0。1
:
6379
>
pfadd
key2
c
j
k
l
m
e
g
a
#
建立第二組元素
(
integer
)
1
127。0。0。1
:
6379
>
pfcount
key2
(
integer
)
8
127。0。0。1
:
6379
>
pfmerge
key3
key1
key2
#
合併兩組:
key1
key2
-
>
key3
並集
OK
127。0。0。1
:
6379
>
pfcount
key3
(
integer
)
13
2。2 Bitmap (位儲存)
Bitmap 即點陣圖資料結構,都是操作二進位制位來進行記錄,只有0 和 1 兩個狀態。
用來解決什麼問題
?
比如:統計使用者資訊,活躍,不活躍! 登入,未登入! 打卡,不打卡!
兩個狀態的,都可以使用 Bitmaps
!
如果儲存一年的打卡狀態需要多少記憶體呢? 365 天 = 365 bit 1位元組 = 8bit 46 個位元組左右!
相關命令使用
使用bitmap 來記錄 週一到週日的打卡! 週一:1 週二:0 週三:0 週四:1 ……
127。0。0。1
:
6379
>
setbit
sign
0
1
(
integer
)
0
127。0。0。1
:
6379
>
setbit
sign
1
1
(
integer
)
0
127。0。0。1
:
6379
>
setbit
sign
2
0
(
integer
)
0
127。0。0。1
:
6379
>
setbit
sign
3
1
(
integer
)
0
127。0。0。1
:
6379
>
setbit
sign
4
0
(
integer
)
0
127。0。0。1
:
6379
>
setbit
sign
5
0
(
integer
)
0
127。0。0。1
:
6379
>
setbit
sign
6
1
(
integer
)
0
檢視某一天是否有打卡!
127。0。0。1
:
6379
>
getbit
sign
3
(
integer
)
1
127。0。0。1
:
6379
>
getbit
sign
5
(
integer
)
0
統計操作,統計 打卡的天數!
127。0。0。1
:
6379
>
bitcount
sign
#
統計這周的打卡記錄,就可以看到是否有全勤!
(
integer
)
3
2。3 geospatial (地理位置)
Redis 的 Geo 在 Redis 3。2 版本就推出了! 這個功能可以推算地理位置的資訊: 兩地之間的距離, 方圓幾里的人
2。3。1 geoadd
新增地理位置
127。0。0。1
:
6379
>
geoadd
china
:
city
118。76
32。04
manjing
112。55
37。86
taiyuan
123。43
41。80
shenyang
(
integer
)
3
127。0。0。1
:
6379
>
geoadd
china
:
city
144。05
22。52
shengzhen
120。16
30。24
hangzhou
108。96
34。26
xian
(
integer
)
3
規則
兩級無法直接新增,我們一般會下載城市資料(這個網址可以查詢 GEO:
www。jsons。cn/lngcode
)!
有效的經度從-180度到180度。
有效的緯度從-85。05112878度到85。05112878度。
#
當座標位置超出上述指定範圍時,該命令將會返回一個錯誤。
127。0。0。1
:
6379
>
geoadd
china
:
city
39。90
116。40
beijin
(
error
)
ERR
invalid
longitude
,
latitude
pair
39。900000
,
116。400000
2。3。2 geopos
獲取指定的成員的經度和緯度
127。0。0。1
:
6379
>
geopos
china
:
city
taiyuan
manjing
1
)
1
)
“112。54999905824661255”
1
)
“37。86000073876942196”
2
)
1
)
“118。75999957323074341”
1
)
“32。03999960287850968”
獲得當前定位, 一定是一個座標值!
2。3。3 geodist
如果不存在, 返回空
單位如下
m
km
mi 英里
ft 英尺
127。0。0。1
:
6379
>
geodist
china
:
city
taiyuan
shenyang
m
“1026439。1070”
127。0。0。1
:
6379
>
geodist
china
:
city
taiyuan
shenyang
km
“1026。4391”
2。3。4 georadius
附近的人 ==> 獲得所有附近的人的地址, 定位, 透過半徑來查詢
獲得指定數量的人
127。0。0。1
:
6379
>
georadius
china
:
city
110
30
1000
km
以
100
,
30
這個座標為中心, 尋找半徑為
1000km
的城市
1
)
“xian”
2
)
“hangzhou”
3
)
“manjing”
4
)
“taiyuan”
127。0。0。1
:
6379
>
georadius
china
:
city
110
30
500
km
1
)
“xian”
127。0。0。1
:
6379
>
georadius
china
:
city
110
30
500
km
withdist
1
)
1
)
“xian”
2
)
“483。8340”
127。0。0。1
:
6379
>
georadius
china
:
city
110
30
1000
km
withcoord
withdist
count
2
1
)
1
)
“xian”
2
)
“483。8340”
3
)
1
)
“108。96000176668167114”
2
)
“34。25999964418929977”
2
)
1
)
“manjing”
2
)
“864。9816”
3
)
1
)
“118。75999957323074341”
2
)
“32。03999960287850968”
引數 key 經度 緯度 半徑 單位 [顯示結果的經度和緯度] [顯示結果的距離] [顯示的結果的數量]
2。3。5 georadiusbymember
顯示與指定成員一定半徑範圍內的其他成員
127。0。0。1
:
6379
>
georadiusbymember
china
:
city
taiyuan
1000
km
1
)
“manjing”
2
)
“taiyuan”
3
)
“xian”
127。0。0。1
:
6379
>
georadiusbymember
china
:
city
taiyuan
1000
km
withcoord
withdist
count
2
1
)
1
)
“taiyuan”
2
)
“0。0000”
3
)
1
)
“112。54999905824661255”
2
)
“37。86000073876942196”
2
)
1
)
“xian”
2
)
“514。2264”
3
)
1
)
“108。96000176668167114”
2
)
“34。25999964418929977”
引數與 georadius 一樣
2。3。6 geohash(較少使用)
該命令返回11個字元的hash字串
127。0。0。1
:
6379
>
geohash
china
:
city
taiyuan
shenyang
1
)
“ww8p3hhqmp0”
2
)
“wxrvb9qyxk0”
將二維的經緯度轉換為一維的字串, 如果兩個字串越接近, 則距離越近
2。3。7 底層
geo底層的實現原理實際上就是Zset, 我們可以透過Zset命令來操作geo
127。0。0。1
:
6379
>
type
china
:
city
zset
檢視全部元素 刪除指定的元素
127。0
。
0
。
1
:
6379
>
zrange
china:
city
0
-
1
withscores
1
)
“xian”
2
)
“4040115445396757”
3
)
“hangzhou”
4
)
“4054133997236782”
5
)
“manjing”
6
)
“4066006694128997”
7
)
“taiyuan”
8
)
“4068216047500484”
9
)
“shenyang”
1
)
“4072519231994779”
2
)
“shengzhen”
3
)
“4154606886655324”
127。0
。
0
。
1
:
6379
>
zrem
china:
city
manjing
(
integer
)
1
127。0
。
0
。
1
:
6379
>
zrange
china:
city
0
-
1
1
)
“xian”
2
)
“hangzhou”
3
)
“taiyuan”
4
)
“shenyang”
5
)
“shengzhen”