集合對象的編碼可以是?`intset`?或者?`hashtable`?。
`intset`?編碼的集合對象使用整數集合作為底層實現, 集合對象包含的所有元素都被保存在整數集合里面。
舉個例子, 以下代碼將創建一個如圖 8-12 所示的?`intset`?編碼集合對象:
~~~
redis> SADD numbers 1 3 5
(integer) 3
~~~

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

## 編碼的轉換
當集合對象可以同時滿足以下兩個條件時, 對象使用?`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"
~~~
## 集合命令的實現
因為集合鍵的值為集合對象, 所以用于集合鍵的所有命令都是針對集合對象來構建的, 表 8-10 列出了其中一部分集合鍵命令, 以及這些命令在不同編碼的集合對象下的實現方法。
* * *
表 8-10 集合命令的實現方法
| 命令 | `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`?函數, 從字典中刪除所有鍵為給定元素的鍵值對。 |
- 介紹
- 前言
- 致謝
- 簡介
- 第一部分:數據結構與對象
- 簡單動態字符串
- SDS 的定義
- SDS 與 C 字符串的區別
- SDS API
- 重點回顧
- 參考資料
- 鏈表
- 鏈表和鏈表節點的實現
- 鏈表和鏈表節點的 API
- 重點回顧
- 字典
- 字典的實現
- 哈希算法
- 解決鍵沖突
- rehash
- 漸進式 rehash
- 字典 API
- 重點回顧
- 跳躍表
- 跳躍表的實現
- 跳躍表 API
- 重點回顧
- 整數集合
- 整數集合的實現
- 升級
- 升級的好處
- 降級
- 整數集合 API
- 重點回顧
- 壓縮列表
- 壓縮列表的構成
- 壓縮列表節點的構成
- 連鎖更新
- 壓縮列表 API
- 重點回顧
- 對象
- 對象的類型與編碼
- 字符串對象
- 列表對象
- 哈希對象
- 集合對象
- 有序集合對象
- 類型檢查與命令多態
- 內存回收
- 對象共享
- 對象的空轉時長
- 重點回顧
- 第二部分:單機數據庫的實現
- 數據庫
- 數據庫鍵空間
- 重點回顧
- RDB 持久化
- RDB 文件結構
- 重點回顧
- AOF 持久化
- AOF 持久化的實現
- 重點回顧
- 事件
- 文件事件
- 重點回顧
- 參考資料
- 客戶端
- 客戶端屬性
- 重點回顧
- 服務器
- 命令請求的執行過程
- 重點回顧
- 第三部分:多機數據庫的實現
- 復制
- 舊版復制功能的實現
- 重點回顧
- Sentinel
- 啟動并初始化 Sentinel
- 重點回顧
- 參考資料
- 集群
- 節點
- 重點回顧
- 第四部分:獨立功能的實現
- 發布與訂閱
- 頻道的訂閱與退訂
- 重點回顧
- 參考資料
- 事務
- 事務的實現
- 重點回顧
- Lua 腳本
- 創建并修改 Lua 環境
- 重點回顧
- 排序
- SORT <key> 命令的實現
- 重點回顧
- 二進制位數組
- GETBIT 命令的實現
- 重點回顧
- 慢查詢日志
- 慢查詢記錄的保存
- 慢查詢日志的閱覽和刪除
- 添加新日志
- 重點回顧
- 監視器
- 成為監視器
- 向監視器發送命令信息
- 重點回顧
- 源碼、相關資源和勘誤