文章目錄
1. 前言
Redis
不是簡單的鍵值存儲,它實際上是一個數據結構服務器,支持不同類型的值。在傳統鍵值存儲中,鍵和值都是字符串,而在Redis
中,值不僅限於簡單的字符串,還可以容納更復雜的數據結構。
2. Redis的數據類型
Redis中的鍵
Redis的鍵是二進制安全的,這意味着您可以使用任何二進制序列作爲鍵,從“ foo”之類的字符串到JPEG文件的內容。 空字符串也是有效的鍵。
需要注意的是:
- 儘量不要使用過長的鍵。例如,一個1024字節的鍵會佔用較大的內存,而且在比較鍵的過程中代價更高。更好的方法是,將該值hash之後再作爲鍵。
- 儘量不要使用過短的鍵。儘管較短的鍵佔用更少的內存,但是可讀性更差。例如: “user:1000:followers” 比 “u1000flw” 作爲鍵更合適。
- 鍵的模式最好保持一致。 例如,“ object-type:id”是一個好主意,例如“ user:1000”。 點或破折號通常用於多詞字段,例如“ comment: 1234:reply.to”或“ comment: 1234:reply-to”。
鍵的大小不能超過512 MB
。
2.1 String
Redis
中最簡單的數據類型就是 String
了,而它也是 Memcached
中唯一的數據類型。
由於Redis
中的鍵都是 String
類型的,當值也是 String
時,其實就是將一個 String
映射到另一個 String
。
值的最大值不能超過512MB
。
String
類型是二進制安全的,表示redis
的 string
可以包含任何數據。如數字,字符串,jpg圖片或者序列化的對象。
# 如果原來的key已經存在了,那麼再使用set會給key設置一個新的值。
set 鍵 值
get 鍵
# 該鍵的值加1(原子性遞增),將原鍵的值解析爲一個整數,然後加1,將加1後的結果SET爲該鍵的新值
incr 鍵
# 在原來鍵的值得基礎上,追加數據
append 鍵 "append value"
# 獲取字符串的值得分片, [start, end]
getrange 鍵 start end
# 設置字符串的值的分片,從索引爲start開始 用新值開始替換
setrange 鍵 start 新值
INCR
, DECR
, INCRBY
是原子性操作
,也就是說不會類似多線程的現象。例如,多線程中,A和B同時讓共享變量x(值爲10)加1,結果可能會是11,而不是12,而兩個 redis-cli
同時使用incr
對某鍵加1,最終的結果一定是12。
1條命令 SET
或 GET
多個鍵的值。
mset key1 value1 key2 value2 key3 value3
mget key1 key2 key3
# EXISTS返回1或者0作爲信號,表示該鍵是否存在
exists 鍵
# DEL刪除指定鍵和其值
del 鍵
mget
的返回值是一個數組。
2.2 Lists
Redis
中的 lists
是通過雙端鏈表實現的。這意味着不管 list
中的元素有多少,在列頭和列尾添加一個新元素都能在常數時間,即O(1),內完成。lists
中的元素都是字符串。
1個 Redis list
的最大長度限制爲 232 - 1
之所以使用鏈表實現,就是因爲常常需要在較短時間內從很長很長的 list
中添加或刪除元素(頭、尾都行)。
還有一個優點是,Redis
中的 Lists
在常數時間,即O(1),內獲取固定長度的list
。
但是缺點是獲取元素比數組實現的 list
要慢,尤其是獲取中間的元素,這是一個O(N)操作。
# 添加一個新元素或多個元素到列頭
lpush list名 值
lpush list名 值1 值2 值3
# 添加一個新元素或多個元素到列尾
rpush list名 值
rpush list名 值1 值2 值3
# 從列表中獲取一定範圍的數據,[start, end]
lrange list名 start end
# 返回list中的所有元素
lrange list名 0 -1
# 彈出(獲取+刪除)list中的元素
# 從列頭刪除1個元素
lpop list名
# 從列尾刪除1個元素
rpop list名
需要注意的是LRANGE命令中,[start, end]區間兩邊都是閉合的。
使用LPOP或者RPOP刪除list中元素,若list已經爲空,則返回值是NULL。
常見用例
- 記錄用戶發佈到社交網絡上的最近更新。
生產者-消費者模式
。生產者生產消息並寫入list
中,消費者消費消息。
舉個例子,假設你的微博主頁顯示了你發佈的一些最新照片,並且你希望主頁的訪問速度要儘可能的快。利用 Redis
中的 list
來實現:
- 每次用戶發佈新照片時,我們都會使用
LPUSH
將其照片id
添加到list
中。 - 當用戶訪問主頁時,我們使用
LRANGE 0 9
來獲取最新發布的10個照片。
Capped lists
在許多情況,我們只是想使用Redis
中的list
來存儲最新的數據,不管這些數據是社交網絡更新數據、日誌數據還是其它什麼數據。
只記錄最新的N個數據,使用LTRIM
丟棄所有舊的數據。LTRIM
命令指定一個範圍,這個範圍之外的元素都會被刪掉。
# 向某list中寫入5個元素
rpush list名 1 2 3 4 5
# 設置最新值的範圍是[0, 2],兩邊都是閉合的,所以有3個元素
ltrim list名 0 2
# 展示該list中的所有元素
lrange list名 0 -1
# Redis中的Sorted sets
在Redis中獲取集合中間某部分的元素是很常見的,這就需要使用Sorted sets。
list的阻塞操作
用Redis
中的list
實現隊列非常合適,這是因爲list
有一個特性:阻塞操作,並且通常用作進程間通信系統的構建塊。
- 生產者調用
LPUSH
命令推送一些數據到list中 - 消費者調用
RPOP
從該list
中抽取/處理這些數據
但是,有時list
可能爲空(沒有任何要處理的數據),因此RPOP
會返回NULL
。 在這種情況下,消費者被迫等待一段時間,然後使用RPOP
重試。這被稱爲輪詢
,在這種情形有幾個缺點:
Redis
和clients
會處理這些讀命令,但這並有什麼卵用,因爲壓根就沒有數據了。- 由於添加了數據處理的延遲,當1個消費者獲取到一個
NULL
後,會縮短延遲的時間,這樣會導致更多無用命令的調用。
Redis
使用BRPOP
和BLPOP
可以解決上述問題。BRPOP
和BLPOP
是RPOP
和LPOP
的阻塞版本,當list
是空的之後,會阻塞獲取數據。僅當將新元素添加到list
中或達到用戶指定的超時時間了,它們纔會返回結果給調用方。
可以同時等待多個list
# 意思是等待該list中的元素,但如果5秒鐘後沒有可用元素,則返回
brpop list名 5
# 等待時間設置爲0s,表示永遠等待
brpop list名 0
-clients
按照順序享受服務。當該list
中有可用元素之後,會優先服務第一個阻塞的客戶端。
- 返回值與
RPOP
相比有所不同。BRPOP
的返回值是一個數組,由鍵名和值,因爲BRPOP
和BLPOP
能夠同時等待多個list,所以需要指明list
名。 - 如果超時了,則會返回
NULL
。
自動創建和刪除鍵
我們不必去創建空list
,只需要向該list
中添加元素即可。我們也不用移除空list
,這些都是Redis
自己的工作。Streams
, Sets, Sorted Sets
和 Hashes
也是這樣。
- 當我們添加元素到一個
聚合數據類型
(Streams
,Sets
,Sorted Sets
和Hashes
),如果該鍵並不存在,則Redis
會先建一個空的該聚合數據類型作爲鍵,然後將元素添加進去。
- 當我們從一個聚合數據類型中刪除了最後一個元素之後,
Redis
會自動刪除該鍵。Stream
數據類型是個例外。
- 對一個空的
list
調用只讀命令(如LLEN
,返回list
的長度)或者寫命令移除元素時,效果如下。
2.3 Hashes
Redis
哈希是字符串字段和字符串值之間的映射,1個 hash
常用來表示1個對象(可以有許多 field
和 對應的 value
)下面的例子中,鍵是"user:1000",剩下是一對一對的字段和值。
每一個hash
可以存儲 232 -1 個 field
/value
對。
# 爲某hash鍵設置字段的值
hmset 鍵 field1 value1 field2 value2
# 獲取該hash鍵的某一個字段的值
hget 鍵 field1
hget 鍵 field2
# 獲取該hash鍵的多個字段的值,返回的是一個數組
hmget 鍵 field1 field3 field2
# 獲取該hash鍵的所有字段和其值
hgetall 鍵
# 有些命令可以單獨在hash鍵的某字段上進行操作,如HINCRBY(理解爲h-incr-by)
hincrby 鍵 field1 10 # 讓該hash鍵的field1字段的值加10
需要注意的是
,比較值比較小的hashes使用了特殊的編碼方式,使得其在內存中的存儲效率更高。
2.4 Sets
集合類型也是用來保存多個字符串的元素,可以理解成python中的set。具有以下特性:
- 沒有重複的元素
- 集合中的元素是無序的,不能通過索引下標獲取元素
- 支持集合間的操作,可以取多個集合取交集、並集、差集
Redis
中的 Sets
是字符串的無序集合。它支持集合間的操作(交集、並集、差集)和成員檢查等操作。
1個 Redis set
的最大長度限制爲 232 - 1
常見用例
- 跟蹤指定博客的所有IP地址,會自動去重。
Redis sets
可以很好的表示某些關係。用set
來表示每一個tag
- 可以從 set 中獲取隨機元素。
# 向set中添加一個或多個新元素
sadd set名 value1 value2 value3
# 查看某set鍵中的所有元素
smembers set名
# 判斷某元素是否在該set中
sismember set名 value
# 求交集
sinter set名1 set名2 set名3
# 求並集
sunion set名1 set名2 set名3
sunionstore 新set名 set名1 set名2 # 將幾個set的並集存到一個新的set
# 求差集
# 彈出集合中隨機一個元素
spop set名
# 獲取集合中隨機一個元素但是不刪除
srandmember set名
# 集合的基數,即集合中元素數量
scard set名
例如,打標籤。
# 表示給id爲1000的新聞文章打了標籤號爲1,2,5,77的標籤。
sadd news:1000:tags 1 2 5 77
# 也可以反過來,記錄該標籤標記了哪些新聞文章
sadd tag:1:news 1000
sadd tag:2:news 1000
sadd tag:5:news 1000
sadd tag:77news 1000
2.5 Sorted Sets
Sorted Sets
即有序的set
。這個有序是通過給每個元素關聯一個名爲score
的浮點數來實現的,score
是可以相同的,也就是score
可以重複。
排序規則如下:
- A和B是兩個不同score的元素,如果A.score > B.score,則A > B
- 如果A和B的score相同,比較A和B的字符串大小,若A的字符串大於B,則A > B
Sorted set
的特點可以簡單總結爲:添加、刪除、更新快,元素有序,成員檢查快,獲取中間元素快。
更新分數只需要zadd
爲該元素設置新的score
即可,但是在更新score
之後,Sorted set
需要O(log(N)
的時間複雜度進行重新排序。因此,當有大量更新時,使用sorted set
是一個合適的選擇。
# 給某Sorted set添加一個或多個元素
zadd zset名 score1 value1 score2 value2 score3 value3
# 查看某Sorted set中的所有元素,分數從小到大
zrange zset名 0 -1
# 查看某Sorted set中的所有元素,分數從大到小
zrevrange zset名 0 -1
# 使用withscores返回元素的同時也返回其分數
zrange zset名 0 -1 withscores
############# 範圍操作 #############################
# 返回score在[負無窮,100] 左右區間都包括,這個範圍的元素
zrangebyscore zset名 -inf 100
zrangebyscore zset名 100 +inf # [100, +inf]
# 移除某範圍[start, end] 左右區間都包括,的元素,並返回
zremrangebyscore zset名 start end
# 獲得值爲value的元素在該sorted set中的排名
zrank zset名 value
zrevrank zset名 value # 逆序的排名
############# 按字典順序(字符串大小順序)獲取範圍 #############################
# 當分數都相同時,按照字符串的大小順序有序。
zrangebylex zset名 min max
zrevrangebylex zset名 min max
zremrangebylex zset名 min max
zlexcount zset名 min max
常見用例
- 大型在線遊戲的排行榜。使用
ZADD
可以很容易地更新用戶的score
,還可以用ZRANGE
獲取排名前幾的用戶,用ZRANK
獲取某用戶的排名。 - 索引數據。比如,我有很多代表用戶的
hash
,那麼可以將用戶的ID作爲Sorted set
的值,用戶的年齡作爲Sorted set
的score
。然後使用ZRANGEBYSCORE
就可以快速獲取指定年齡範圍的用戶了。
2.6 Bitmaps
位圖。並不是一種實際的數據類型,而是在String類型上定義的一組面向位的操作。
位操作分爲兩類:常數時間的單個位操作(如將一個位設置爲1或0或獲取其值),以及對一組位的操作,例如,計算給定位範圍內位爲1或者0的數量 (例如,人口計數)。
位圖的最大優點之一是,它們在存儲信息時通常可以節省大量空間。 例如,在以增量用戶ID表示不同用戶的系統中,僅使用512 MB內存就可以記住40億用戶的一位信息(例如,知道用戶是否要接收新聞通訊)。
如果設置或者獲取的位數超過了當前String
的長度,那麼Redis
會自動擴大該String
的長度。
########### 單個位上的操作 #######################
setbit 某key 第幾位 值 # 例如: setbit mykey 10 1. 意思第10位設爲1
# 這是該key第10位的值爲1
setbit 某key 10 1
# 獲取第10位的值
getbit 某key 10
# 因爲第11位沒有設置,所以是0
getbit 某key 11
########### 多個位上的操作 #######################
# 在不同String上,實現按位運算,如AND OR XOR NOT操作
# 對一個或多個保存二進制位的字符串key進行位元操作,並將結果保存到destkey上。
bitop 操作 destkey key1 key2 key3
# 統計爲1的位的數量
bitcount 某key
# 查找第一爲0或者爲1的位
bitpos 某key 0或者1
2.7 HyperLogLogs
HLL
是一種概率數據結構,旨在對唯一事物進行計數。
在Redis
中HLL
被編碼爲Redis
字符串,所以我們可以用GET
命令來序列化HLL
,可以用SET
命令來反序列化成HLL
。
從概念上講,HLL API
就像使用Set
來執行相同的任務。 你可以將元素添加到集合中,並使用SCARD
檢查集合中的元素數量,這是唯一的,因爲SADD
不會重新添加現有元素。
# 將新元素添加到該hll鍵中
pfadd hll鍵 value1 value2 value3
# 該hll鍵的值的數量
pfcount hll鍵
常見用例
- 統計每天用戶在搜索表單中執行的唯一查詢。
2.8 Streams
Stream
是Redis5.0
之後引入的新數據類型。提供抽象日誌數據的append-only
數據結構。從概念上講,因爲Redis
是流式傳輸在內存中表示的抽象數據類型,所以它們實現了更強大的操作,以克服日誌文件本身的限制。
consumer group
: 允許一組客戶端合作使用同一消息流的不同部分。
3. Redis過期
Redis
中的鍵都有存活時間。這與Redis
中的數據結構無關。我們可以爲鍵設置一個過期時間,當超過這個過期時間會自動刪除這個鍵和其值,就像使用DEL
刪除了這個鍵一樣。
默認永不過期
。
- 過期時間的精度可以是s或者ms。
- 只要超過過期時間1ms則認爲過期。
- 過期信息是複製並持久化在磁盤中的
設置過期時間,過期時間的默認單位是S
# 設置過期時間
set 鍵 值
expire 鍵 過期時間的s值
pexpire 鍵 過期時間的ms值
或者使用SET命令的ex選項
set 鍵 值 ex 過期時間的s值
# 查看某鍵還有多少時間過期
ttl 鍵 # s
pttl 鍵 # ms
# 讓某鍵用不過期
persist 鍵
參考文獻
[1] Redis 官方文檔