一、前言
Redis 提供了5種數據類型:String(字符串)、Hash(哈希)、List(列表)、Set(集合)、Zset(有序集合),理解每種數據類型的特點對於redis的開發和運維非常重要。
Redis 中的 Set 是我們經常使用到的一種數據類型,根據使用方式的不同,可以應用到很多場景中。
二、底層實現
集合對象的編碼可以是 intset 或者 hashtable 。
intset 編碼的集合對象使用整數集合作為底層實現, 集合對象包含的所有元素都被保存在整數集合里面。
舉個例子, 以下代碼將創建一個如圖 8-12 所示的 intset 編碼集合對象:
redis> SADD numbers 1 3 5
(integer) 3
結構圖 8-12:

另一方面, hashtable 編碼的集合對象使用字典作為底層實現, 字典的每個鍵都是一個字符串對象, 每個字符串對象包含了一個集合元素, 而字典的值則全部被設置為 NULL 。
舉個例子, 以下代碼將創建一個如圖 8-13 所示的 hashtable 編碼集合對象:
redis> SADD fruits "apple" "banana" "cherry"
(integer) 3
結構圖 8-13:

三、編碼轉換
當集合對象可以同時滿足以下兩個條件時, 對象使用 intset 編碼:
1.集合對象保存的所有元素都是整數值;
2.集合對象保存的元素數量不超過 512 個;
不能滿足這兩個條件的集合對象需要使用 hashtable 編碼。
注意 : 第二個條件的上限值是可以修改的, 具體請看配置文件中關於 set-max-intset-entries 選項的說明。對於使用 intset 編碼的集合對象來說, 當使用 intset 編碼所需的兩個條件的任意一個不能被滿足時, 對象的編碼轉換操作就會被執行: 原本保存在整數集合中的所有元素都會被轉移並保存到字典里面, 並且對象的編碼也會從 intset 變為 hashtable。
舉個例子, 以下代碼創建了一個只包含整數元素的集合對象, 該對象的編碼為 intset :
redis> SADD numbers 1 3 5
(integer) 3
redis> OBJECT ENCODING numbers
"intset"
不過, 只要我們向這個只包含整數元素的集合對象添加一個字符串元素,集合對象的編碼轉移操作就會被執行
redis> SADD numbers "seven"
(integer) 1
redis> OBJECT ENCODING numbers
"hashtable"
除此之外, 如果我們創建一個包含 512 個整數元素的集合對象, 那么對象的編碼應該會是 intset :
redis> EVAL "for i=1, 512 do redis.call('SADD', KEYS[1], i) end" 1 integers
(nil)
redis> SCARD integers
(integer) 512
redis> OBJECT ENCODING integers
"intset"
但是, 只要我們再向集合添加一個新的整數元素, 使得這個集合的元素數量變成 513 , 那么對象的編碼轉換操作就會被執行:
redis> SADD integers 10086
(integer) 1
redis> SCARD integers
(integer) 513
redis> OBJECT ENCODING integers
"hashtable"
四、命令實現
因為集合鍵的值為集合對象, 所以用於集合鍵的所有命令都是針對集合對象來構建的, 以下表格列出了其中一部分集合鍵命令, 以及這些命令在不同編碼的集合對象下的實現方法。
命令 | intset 編碼的實現方法 | hashtable 編碼的實現方法 |
---|---|---|
SADD | 調用 intsetAdd 函數, 將所有新元素添加到整數集合里面。 | 調用 dictAdd , 以新元素為鍵, NULL 為值, 將鍵值對添加到字典里面。 |
SCARD | 調用 intsetLen 函數, 返回整數集合所包含的元素數量, 這個數量就是集合對象所包含的元素數量。 | 調用 dictSize 函數, 返回字典所包含的鍵值對數量, 這個數量就是集合對象所包含的元素數量。 |
SISMEMBER | 調用 intsetFind 函數, 在整數集合中查找給定的元素, 如果找到了說明元素存在於集合, 沒找到則說明元素不存在於集合。 | 調用 dictFind 函數, 在字典的鍵中查找給定的元素, 如果找到了說明元素存在於集合, 沒找到則說明元素不存在於集合。 |
SMEMBERS | 遍歷整個整數集合, 使用 intsetGet 函數返回集合元素。 | 遍歷整個字典, 使用 dictGetKey 函數返回字典的鍵作為集合元素。 |
SRANDMEMBER | 調用 intsetRandom 函數, 從整數集合中隨機返回一個元素。 | 調用 dictGetRandomKey 函數, 從字典中隨機返回一個字典鍵。 |
SPOP | 調用 intsetRandom 函數, 從整數集合中隨機取出一個元素, 在將這個隨機元素返回給客戶端之后, 調用 intsetRemove 函數, 將隨機元素從整數集合中刪除掉。 | 調用 dictGetRandomKey 函數, 從字典中隨機取出一個字典鍵, 在將這個隨機字典鍵的值返回給客戶端之后, 調用dictDelete 函數, 從字典中刪除隨機字典鍵所對應的鍵值對。 |
SREM | 調用 intsetRemove 函數, 從整數集合中刪除所有給定的元素。 | 調用 dictDelete 函數, 從字典中刪除所有鍵為給定元素的鍵值對。 |
五、應用場景
1.抽獎
抽獎
1)用戶參與抽獎:SADD order 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010
2)查看所有參與抽獎的人:SMEMBERS order
3)重復抽獎每次抽取兩人:SMEMBERS order 2
4)不重復抽獎,三等獎3人,二等獎2人,一等獎1人
SPOP order 3
SPOP order 2
SPOP order 1
2.點贊、收藏、標簽
點贊、收藏、標簽
1)點贊的人:SADD like:1 1001 1002 1003 1004 1005
2)取消點贊:SREM like:1 1002
3)檢查用戶是否點贊過:
SISMEMBER like:1 1002
SISMEMBER like:1 1005
4)獲取點贊人員列表:SMEMBERS like:1
5)獲取點贊總人數:SCARD like:1
3.關注模型
redis> SADD wangwu zhangsan lisi zhaoliu haoba
(integer) 4
redis> SADD zhangsan lisi wangwu sijiu
(integer) 3
redis> SADD lisi zhaoliu zhangsan qinshi
(integer) 3
redis> SINTER wangwu zhangsan
1) "lisi"
redis> SISMEMBER zhangsan lisi
(integer) 1
redis> SISMEMBER lisi zhangsan
(integer) 1
redis> SISMEMBER zhaoliu zhangsan
(integer) 0
redis> SISMEMBER haoba zhangsan
(integer) 0
redis> SDIFF zhangsan wangwu
1) "sijiu"
2) "wangwu"
redis> SDIFF lisi wangwu
1) "qinshi"
六、要點總結
(1)集合對象的編碼可以是 intset 或者 hashtable 。
(2)intset 編碼的集合對象使用整數集合作為底層實現。
(3)hashtable 編碼的集合對象使用字典作為底層實現。
(4)intset 與 hashtable 編碼之間,符合條件的情況下,可以轉換。
over
