集合(set
)類型也是用來保存多個 字符串元素,但和 列表類型 不同的是,集合中 不容許有重複元素,而且集合中的元素是 無序的,不能經過 索引下標 獲取元素。編程
如圖所示,集合 user:1:follow
包含着 "it"
、"music"
、"his"
、"sports"
四個元素,一個 集合 最多能夠存儲 2 ^ 32 - 1
個元素。Redis
除了支持 集合內 的 增刪改查,同時還支持 多個集合 取 交集、並集、差集。合理地使用好集合類型,能在實際開發中解決不少實際問題。後端
sadd key element [element ...]
返回結果爲添加成功的 元素個數,例如:
127.0.0.1:6379> exists myset
(integer) 0
127.0.0.1:6379> sadd myset a b c
(integer) 3
127.0.0.1:6379> sadd myset a b
(integer) 0
複製代碼
srem key element [element ...]
返回結果爲成功刪除 元素個數,例如:
127.0.0.1:6379> srem myset a b
(integer) 2
127.0.0.1:6379> srem myset hello
(integer) 0
複製代碼
scard key
scard
的 時間複雜度 爲 O(1)
,它 不會遍歷 集合全部元素,而是直接用 Redis
的 內部 的變量,例如:
127.0.0.1:6379> scard myset
(integer) 1
複製代碼
sismember key element
若是給定元素 element
在集合內返回 1
,反之返回 0
。
127.0.0.1:6379> sismember myset c
(integer) 1
複製代碼
srandmember key [count]
[count]
是 可選參數,若是不寫默認爲 1
。
127.0.0.1:6379> srandmember myset 2
1) "a"
2) "c"
127.0.0.1:6379> srandmember myset
"d"
複製代碼
spop key
spop
操做能夠從 集合 中 隨機彈出 一個元素,例以下面代碼是一次 spop
後,集合元素 變爲 "d b a"
。
127.0.0.1:6379> spop myset
"c"
127.0.0.1:6379> smembers myset
1) "d"
2) "b"
3) "a"
複製代碼
注意:
Redis
從3.2
版本開始,spop
也支持[count]
參數。
srandmember
和 spop
都是 隨機 從集合選出元素,二者不一樣的是 spop
命令執行後,元素 會從集合中 刪除,而 srandmember
不會刪除元素。
smembers key
下面代碼獲取集合 myset
的 全部元素,而且 返回結果 是 無序的。
127.0.0.1:6379> smembers myset
1) "d"
2) "b"
3) "a"
複製代碼
smembers
和 lrange
、hgetall
都屬於 比較重 的命令,若是 元素過多 存在 阻塞 Redis
的可能性,這時候能夠使用 sscan
命令來完成。
如今有 兩個集合,它們分別是 user:1:follow
和 user:2:follow
。
127.0.0.1:6379> sadd user:1:follow it music his sports
(integer) 4
127.0.0.1:6379> sadd user:2:follow it news ent sports
(integer) 4
複製代碼
sinter key [key ...]
下面的代碼是求 user:1:follow
和 user:2:follow
兩個集合的 交集,返回結果是 sports
、it
。
127.0.0.1:6379> sinter user:1:follow user:2:follow
1) "sports"
2) "it"
複製代碼
suinon key [key ...]
下面的代碼是求 user:1:follow
和 user:2:follow
兩個集合的 並集,返回結果是 sports
、it
、his
、news
、music
、ent
。
127.0.0.1:6379> sunion user:1:follow user:2:follow
1) "sports"
2) "it"
3) "his"
4) "news"
5) "music"
6) "ent"
複製代碼
sdiff key [key ...]
下面的代碼是求 user:1:follow
和 user:2:follow
兩個集合的 差集,返回結果是 music
和 his
。
127.0.0.1:6379> sdiff user:1:follow user:2:follow
1) "music"
2) "his"
複製代碼
前面三個求 交集、並集 和 差集 的操做獲得的結果,如圖所示:
集合間 的運算在 元素較多 的狀況下會 比較耗時,因此 Redis
提供瞭如下 三個命令(原命令 + store
)將 集合間交集、並集、差集 的結果保存在 destination key
中。
sinterstore destination key [key ...] suionstore destination key [key ...] sdiffstore destination key [key ...]
下面的操做會將 user:1:follow
和 user:2:follow
兩個集合 的 交集結果 保存在 user:1_2:inter
中,user:1_2:inter
自己也是 集合類型。
127.0.0.1:6379> sinterstore user:1_2:inter user:1:follow user:2:follow
(integer) 2
127.0.0.1:6379> type user:1_2:inter
set
127.0.0.1:6379> smembers user:1_2:inter
1) "it"
2) "sports"
複製代碼
有關 集合 的 經常使用命令 基本上就是這麼多了,下表給出了 集合經常使用命令 的 時間複雜度,開發人員能夠根據自身需求進行選擇。
集合類型 的 內部編碼 有兩種:
當集合中的元素都是 整數 且 元素個數 小於 set-max-intset-entries
配置(默認 512
個)時,Redis
會選用 intset
來做爲 集合 的 內部實現,從而 減小內存 的使用。
當集合類型 沒法知足 intset
的條件時,Redis
會使用 hashtable
做爲集合的 內部實現。
當元素個數 較少 且都爲 整數 時,內部編碼 爲 intset
。
127.0.0.1:6379> sadd setkey 1 2 3 4
(integer) 4
127.0.0.1:6379> object encoding setkey
"intset"
複製代碼
512
個,內部編碼 變爲 hashtable
。127.0.0.1:6379> sadd setkey 1 2 3 4 5 6 ... 512 513
(integer) 513
127.0.0.1:6379> scard setkey
(integer) 513
127.0.0.1:6379> object encoding listkey
"hashtable"
複製代碼
hashtable
。127.0.0.1:6379> sadd setkey a
(integer) 1
127.0.0.1:6379> object encoding setkey
"hashtable"
複製代碼
有關集合類型的 內存優化 技巧,後面的文章會有專門的講解。
集合類型 比較典型的使用場景是 標籤(tag
)。簡單的舉兩個例子:
一個用戶可能對娛樂、體育比較感興趣,另外一個用戶可能對歷史、新聞比較感興趣,這些興趣點就是標籤。有了這些數據就能夠獲得喜歡同一個標籤的人,以及用戶的共同喜愛的標籤,這些數據對於用戶體驗以及加強用戶黏度比較重要。
一個電子商務的網站會對不一樣標籤的用戶作不一樣類型的推薦,好比對數碼產品比較感興趣的人,在各個頁面或者經過郵件的形式給他們推薦最新的數碼產品,一般會爲網站帶來更多的利益。
下面使用 集合類型 實現 標籤功能。
sadd user:1:tags tag1 tag2 tag5
sadd user:2:tags tag2 tag3 tag5
...
sadd user:k:tags tag1 tag2 tag4
...
複製代碼
sadd tag1:users user:1 user:3
sadd tag2:users user:1 user:2 user:3
...
sadd tagk:users user:1 user:2
...
複製代碼
用戶 和 標籤 的 關係維護 應該在 一個事務內 執行,防止 部分命令失敗 形成的 數據不一致,有關如何將 兩個命令 放在 一個事務 中,後面會介紹
Lua
的使用。
srem user:1:tags tag1 tag5
...
複製代碼
srem tag1:users user:1
srem tag5:users user:1
...
複製代碼
能夠使用 sinter
命令,來計算 用戶共同感興趣 的 標籤。
sinter user:1:tags user:2:tags
複製代碼
上面只是給出了使用 Redis
集合類型 實現 標籤 的基本思路,實際上一個 標籤系統 遠比這個要 複雜 得多,不過 集合類型 的 應用場景 一般爲如下幾種:
命令組合 | 應用場景 |
---|---|
sadd | Tagging(標籤) |
spop/srandmember | Random item(生成隨機數,好比抽獎) |
sadd + sinter | Social Graph(社交需求) |
本文介紹了 Redis
中的 集合 的 一些 基本命令,包括 集合內部的操做命令 和 集合之間的操做命令,其次還介紹了 集合 的 內部編碼 轉換和以 用戶行爲標籤 爲主的 應用場景。
《Redis 開發與運維》
歡迎關注技術公衆號: 零壹技術棧
本賬號將持續分享後端技術乾貨,包括虛擬機基礎,多線程編程,高性能框架,異步、緩存和消息中間件,分佈式和微服務,架構學習和進階等學習資料和文章。