Redis 集合

拿客_三產發表於2017-06-16

簡介

集合(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)

srandmemberspop 都是隨機從集合選出元素,兩者不同的是 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

smemberslrangehgetall 都屬於比較重的命令,如果元素過多存在阻塞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 作為集合的內部實現。
  1. 當元素個數較少且都為整數時,內部編碼為 intset:
coderknock> SADD testIntset 1 2 3 4 5 6
(integer) 6
coderknock> OBJECT ENCODING testIntset
"intset"
  1. 當元素個數超過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`
  1. 當某個元素不為整數時,內部編碼也會變為 hashtable:
coderknock> SADD testIntset a
(integer) 1
coderknock>  OBJECT ENCODING testIntset
"hashtable"

使用場景

  • sdd=Tagging(標籤)
  • spop/srandmember=Random item(生成隨機數,比如抽獎)
  • sadd+sinter=Social Graph(社交需求)

相關文章