簡介
集合(set)型別也是用來儲存多個的字串元素,但和列表型別不一樣的是,集合中不允許有重複元素,並且集合中的元素是無序的,不能通過索引下標獲取元素。一個集合最多可以儲存 $2^{32}-1$ 個元素。Redis除了支援集合內的增刪改查,同時還支援多個集合取交集、並集、差集,合理地使用好集合型別,能在實際開發中解決很多實際問題。
命令
集合內操作
新增元素
SADD
自1.0.0可用。
時間複雜度:O(N),
N
是被新增的元素的數量。
語法:SADD key member [member …]
說明:
將一個或多個 member
元素加入到集合 key
當中,已經存在於集合的 member
元素將被忽略。
假如 key
不存在,則建立一個只包含 member
元素作成員的集合。
當 key
不是集合型別時,返回一個錯誤。
在 Redis 2.4 版本以前的 SADD
命令,都只接受單個 member
值。
返回值:
被新增到集合中的新元素的數量,不包括被忽略的元素。
示例:
coderknock> SADD saddTest add1 add2 add3
(integer) 3
# 檢視集合中所有元素(該命令之後會介紹)
coderknock> SMEMBERS saddTest
1) "add1"
2) "add3"
3) "add2"
# 新增四個元素,其中兩個與之前新增過的元素重複
coderknock> SADD saddTest add1 add4 add3 add5
(integer) 2 # 只成功新增兩個元素(重複的不再新增 )
coderknock> SMEMBERS saddTest
1) "add3"
2) "add2"
3) "add4"
4) "add5"
5) "add1"
刪除元素
SREM
自1.0.0可用。
時間複雜度:O(N),
N
是被新增的元素的數量。
語法:SREM key member [member …]
說明:
移除集合 key
中的一個或多個 member
元素,不存在的 member
元素會被忽略。
當 key
不是集合型別,返回一個錯誤。
在 Redis 2.4 版本以前的 SREM
命令,都只接受單個 member
值。
返回值:
被成功移除的元素的數量,不包括被忽略的元素。
示例:
# 刪除三個元素,其中 sadd7 不存在
coderknock> SREM saddTest add3 add5 add7
(integer) 2
coderknock> SMEMBERS saddTest
1) "add4"
2) "add1"
3) "add2"
# 執行該操作的不是集合元素
coderknock> SREM embstrKey a
(error) WRONGTYPE Operation against a key holding the wrong kind of value
計算元素個數
SCARD
自1.0.0可用。
時間複雜度:O(1)。
語法:SCARD key
說明:
返回集合 key
的基數(集合中元素的數量)。
返回值:
集合的基數。
當 key
不存在時,返回 0
。
示例:
coderknock> SCARD saddTest
(integer) 3
# key 不存在返回 0
coderknock> SCARD add
(integer) 0
# key 型別不是集合時會報錯
coderknock> SCARD embstrKey
(error) WRONGTYPE Operation against a key holding the wrong kind of value
SCARD
命令不會遍歷集合所有元素,而是直接使用 Redis 內部的變數來獲取集合長度。
判斷元素是否存在集合中
SISMEMBER
自1.0.0可用。
時間複雜度:O(1)。
語法:SISMEMBER key member
說明:
判斷 member
元素是否集合 key
的成員。
返回值:
如果 member
元素是集合的成員,返回 1
。
如果 member
元素不是集合的成員,或 key
不存在,返回 0
。
示例:
coderknock> SISMEMBER saddTest add1
(integer) 1
# add7 元素不存在
coderknock> SISMEMBER saddTest add7
(integer) 0
# key 不存在
coderknock> SISMEMBER nonSet a
(integer) 0
# key 型別不是集合
coderknock> SISMEMBER embstrKey a
(error) WRONGTYPE Operation against a key holding the wrong kind of value
隨機從集合中返回指定個數的元素
SRANDMEMBER
自1.0.0可用。
時間複雜度:只提供
key
引數時為 O(1) 。如果提供了
count
引數,那麼為 O(N) ,N 為返回陣列的元素個數。
語法:SRANDMEMBER key [count]
說明:
如果命令執行時,只提供了 key
引數,那麼返回集合中的一個隨機元素。
從 Redis 2.6 版本開始, SRANDMEMBER
命令接受可選的 count
引數:
- 如果
count
為正數,且小於集合基數,那麼命令返回一個包含count
個元素的陣列,陣列中的元素各不相同。如果count
大於等於集合基數,那麼返回整個集合。 - 如果
count
為負數,那麼命令返回一個陣列,陣列中的元素可能會重複出現多次,而陣列的長度為count
的絕對值。
該操作和 SPOP
相似,但 SPOP
將隨機元素從集合中移除並返回,而 SRANDMEMBER
則僅僅返回隨機元素,而不對集合進行任何改動。
返回值:
只提供 key
引數時,返回一個元素;如果集合為空,返回 nil
。
如果提供了 count
引數,那麼返回一個陣列;如果集合為空,返回空陣列。
示例:
coderknock> SADD languageSet java go python kotlin c lua javascript
coderknock> SRANDMEMBER languageSet
"kotlin"
coderknock> SRANDMEMBER languageSet
"go"
coderknock> SRANDMEMBER languageSet 3
1) "go"
2) "python"
3) "javascript"
coderknock> SRANDMEMBER languageSet 3
1) "c"
2) "java"
3) "lua"
# count 超出 長度時返回所有的元素
coderknock> SRANDMEMBER languageSet 8
1) "kotlin"
2) "c"
3) "java"
4) "go"
5) "lua"
6) "python"
7) "javascript"
coderknock> SRANDMEMBER languageSet -2
1) "python"
2) "kotlin"
# key 不存在時返回空集合
coderknock> SRANDMEMBER nonKey 2
(empty list or set)
coderknock> SRANDMEMBER nonKey
(nil)
從集合中隨機彈出元素
SPOP
自1.0.0可用。
時間複雜度:O(1)。
語法:SPOP key [count]
說明:
移除並返回集合中的一個隨機元素。
如果只想獲取一個隨機元素,但不想該元素從集合中被移除的話,可以使用 SRANDMEMBER
命令。
從 Redis 3.2 版本開始, SPOP
命令接受可選的 count
引數
返回值:
被移除的隨機元素。
當 key
不存在或 key
是空集時,返回 nil
。
如果提供了 count
引數,那麼返回一個陣列;如果集合為空,返回空陣列。
示例:
coderknock> SMEMBERS languageSet
1) "kotlin"
2) "c"
3) "java"
4) "go"
5) "javascript"
6) "python"
7) "lua"
# 該命令不支援負數作為引數
coderknock> SPOP languageSet -2
(error) ERR index out of range
coderknock> SPOP languageSet 2
1) "lua"
2) "python"
coderknock> SMEMBERS languageSet
1) "kotlin"
2) "c"
3) "java"
4) "go"
5) "javascript"
coderknock> SPOP nonSet 2
(empty list or set)
coderknock> SPOP nonSet
(nil)
srandmember
和 spop
都是隨機從集合選出元素,兩者不同的是 spop
命令執行後,元素會從集合中刪除,而 srandmember
不會。
獲取所有元素
SMEMBERS
自1.0.0可用。
時間複雜度:O(N),
N
為集合的基數。
語法:SMEMBERS key
說明:
返回集合 key
中的所有成員。
不存在的 key
被視為空集合。
這與執行只有 key
引數的 SINTER
命令效果相同。
返回值:
集合中的所有成員,key
不存在返回空集合。
示例:
coderknock> SISMEMBER saddTest add1
(integer) 1
# add7 元素不存在
coderknock> SISMEMBER saddTest add7
(integer) 0
# key 不存在
coderknock> SISMEMBER nonSet a
(integer) 0
# key 型別不是集合
coderknock> SISMEMBER embstrKey a
(error) WRONGTYPE Operation against a key holding the wrong kind of value
smembers
和 lrange
、hgetall
都屬於比較重的命令,如果元素過多存在阻塞Redis的可能性,這時候可以使用 sscan
(在 Redis 概覽 中有介紹)來完成。
集合間操作
交集
SINTER
自1.0.0可用。
時間複雜度:最差情況:O(N * M),
N
為給定集合當中基數最小的集合,M
為給定集合的個數。
語法:SINTER key [key …]
說明:
返回一個集合的全部成員,該集合是所有給定集合的交集。
不存在的 key
被視為空集。
當給定集合當中有一個空集時,結果也為空集(根據集合運算定律)。
返回值:
交整合員的列表。
示例:
coderknock> SINTER languageSet
1) "kotlin"
2) "c"
3) "java"
4) "go"
5) "javascript"
coderknock> SADD loveLanguageSet java c# c++ kotlin
(integer) 4
coderknock> SINTER languageSet loveLanguageSet
1) "kotlin"
2) "java"
# 不存在的 key 被視為空集
coderknock> SINTER languageSet nonSet
(empty list or set)
並集
SUNION
自1.0.0可用。
時間複雜度:O(N),
N
是所有給定集合的成員數量之和。
語法:SUNION key [key …]
說明:
返回一個集合的全部成員,該集合是所有給定集合的並集。
不存在的 key
被視為空集。
返回值:
並整合員的列表。
示例:
coderknock> SUNION languageSet nonSet loveLanguageSet
1) "kotlin"
2) "c"
3) "c++"
4) "c#"
5) "java"
6) "go"
7) "javascript"
差集
SDIFF
自1.0.0可用。
時間複雜度:O(N),
N
是所有給定集合的成員數量之和。
語法:SDIFF key [key …]
說明:
返回一個集合的全部成員,該集合是所有給定集合之間的差集。
不存在的 key
被視為空集。
返回值:
一個包含差整合員的列表。
示例:
coderknock> SMEMBERS languageSet
1) "kotlin"
2) "c"
3) "java"
4) "go"
5) "javascript"
coderknock> SMEMBERS loveLanguageSet
1) "kotlin"
2) "c++"
3) "c#"
4) "java"
# 一個集合與不存在 key 或者空集合的差集還是該集合本身
coderknock> SDIFF languageSet nonSet
1) "c"
2) "kotlin"
3) "go"
4) "java"
5) "javascript"
coderknock> SDIFF languageSet loveLanguageSet
1) "c"
2) "javascript"
3) "go"
儲存集合運算結果
SINTER
交集、SUNION
並集、SDIFF
差集在集合較多時執行比較耗時,所以 Redis 提供了 原命令 +STORE
的命令可以用來將運算結果進行儲存。
SINTERSTORE
自1.0.0可用。
時間複雜度:O(N * M),
N
為給定集合當中基數最小的集合,M
為給定集合的個數。
語法:SINTERSTORE destination key [key …]
說明:
這個命令類似於 SINTER
命令,但它將結果儲存到 destination
集合,而不是簡單地返回結果集。
如果 destination
集合已經存在,則將其覆蓋。
destination
可以是 key
本身。
返回值:
結果集中的成員數量。
示例:
coderknock> SINTERSTORE sinterStoreTest languageSet loveLanguageSet
(integer) 2
coderknock> SMEMBERS sinterStoreTest
1) "kotlin"
2) "java"
SUNIONSTORE
自1.0.0可用。
時間複雜度:O(N),
N
是所有給定集合的成員數量之和。
語法:SUNIONSTORE destination key [key …]
說明:
這個命令類似於 SUNION
命令,但它將結果儲存到 destination
集合,而不是簡單地返回結果集。
如果 destination
已經存在,則將其覆蓋。
destination
可以是 key
本身。
返回值:
結果集中的成員數量。
示例:
coderknock> SUNIONSTORE sunionStoryTest languageSet nonSet loveLanguageSet
(integer) 7
coderknock> SMEMBERS sunionStoryTest
1) "kotlin"
2) "c"
3) "c++"
4) "c#"
5) "java"
6) "go"
7) "javascript"
SDIFFSTORE
自1.0.0可用。
時間複雜度:O(N),
N
是所有給定集合的成員數量之和。
語法:SDIFFSTORE destination key [key …]
說明:
這個命令的作用和 SDIFF
類似,但它將結果儲存到 destination
集合,而不是簡單地返回結果集。
如果 destination
集合已經存在,則將其覆蓋。
destination
可以是 key
本身。
返回值:
結果集中的元素數量。
示例:
coderknock> SDIFFSTORE sdiffTest languageSet loveLanguageSet
(integer) 3
coderknock> SMEMBERS sdiffTest
1) "c"
2) "javascript"
3) "go"
內部編碼
集合型別的內部編碼有兩種:
- intset(整數集合):當集合中的元素都是整數且元素個數小於 set-maxintset-entries 配置(預設512個)時,Redis 會選用 intset 來作為集合的內部實現,從而減少記憶體的使用。
- hashtable(雜湊表):當集合型別無法滿足 intset 的條件時,Redis 會使用 hashtable 作為集合的內部實現。
- 當元素個數較少且都為整數時,內部編碼為 intset:
coderknock> SADD testIntset 1 2 3 4 5 6
(integer) 6
coderknock> OBJECT ENCODING testIntset
"intset"
- 當元素個數超過512個,內部編碼變為 hashtable:【這裡使用 python 進行測試】
import redis
r = redis.StrictRedis(host=`127.0.0.1`, password=`admin123`, port=6379, db=0)
num = 512
key = "intListTest" + str(num)
r.delete(key)
for i in range(num):
r.sadd(key, i)
# 可以使用這個命令查詢內部編碼
print(key)
print(r.scard(key))
print(r.object("ENCODING", key))
輸出結果:
intListTest512
512
b`intset`
我們將 num 改為 513 輸出結果:
intListTest512
512
b`intset`
- 當某個元素不為整數時,內部編碼也會變為 hashtable:
coderknock> SADD testIntset a
(integer) 1
coderknock> OBJECT ENCODING testIntset
"hashtable"
使用場景
- sdd=Tagging(標籤)
- spop/srandmember=Random item(生成隨機數,比如抽獎)
- sadd+sinter=Social Graph(社交需求)