REDIS_SET 集合 是SADD 、SRANDMEMBER 等命令的操做對象 它使用算法
REDIS_ENCODING_INTSET 和REDIS_ENCODING_HT 兩種方式編碼數據結構
編碼的選擇ide
第一個添加到集合的元素決定了建立集合時所使用的編碼函數
若是第一個元素能夠表示爲long long 類型值也便是它是一個整數那麼集合的初編碼
始編碼爲REDIS_ENCODING_INTSET 。spa
不然集合的初始編碼爲REDIS_ENCODING_HT 。server
編碼的切換對象
若是一個集合使用REDIS_ENCODING_INTSET 編碼那麼當如下任何一個條件被知足時這個blog
集合會被轉換成REDIS_ENCODING_HT 編碼排序
intset 保存的整數值個數超過server.set_max_intset_entries 默認值爲512 。
試圖往集合裏添加一個新元素而且這個元素不能被表示爲long long 類型也便是
它不是一個整數。
字典編碼的集合
當使用REDIS_ENCODING_HT 編碼時集合將元素保存到字典的鍵裏面而字典的值則統一設
爲NULL 。
做爲例子如下圖片展現了一個以REDIS_ENCODING_HT 編碼表示的集合集合的成員爲elem1
、elem2 和elem3
集合命令的實現
Redis 集合類型命令的實現主要是對intset 和dict 兩個數據結構的操做函數的包裝以及
一些在兩種編碼之間進行轉換的函數大部分都沒有什麼須要解釋的地方惟一比較有趣的是
SINTER 、SUNION 等命令之下的算法實現如下三個小節就分別討論它們所使用的算法。
求交集算法
SINTER 和SINTERSTORE 兩個命令所使用的求並交集算法能夠用Python 表示以下
# coding: utf-8 def sinter(*multi_set): # 根據集合的基數進行排序 sorted_multi_set = sorted(multi_set, lambda x, y: len(x) - len(y)) # 使用基數最小的集合做爲基礎結果集有助於下降常數項 result = sorted_multi_set[0].copy() # 剔除全部在sorted_multi_set[0] 中存在 # 但在其餘某個集合中不存在的元素 for elem in sorted_multi_set[0]: for s in sorted_multi_set[1:]: if (not elem in s): result.remove(elem) break return result
算法的複雜度爲O(N2) 執行步數爲S T 其中S 爲輸入集合中基數最小的集合而T 則
爲輸入集合的數量。
求並集算法
SUNION 和SUNIONSTORE 兩個命令所使用的求並集算法能夠用Python 表示以下
# coding: utf-8 def sunion(*multi_set): result = set() for s in multi_set: for elem in s: # 重複的元素會被自動忽略 result.add(elem) return result
算法的複雜度爲O(N) 。
求差集算法
Redis 爲SDIFF 和SDIFFSTORE 兩個命令準備了兩種求集合差的算法。
以Python 代碼表示的算法必定義以下
# coding: utf-8 def sdiff_1(*multi_set): result = multi_set[0].copy() sorted_multi_set = sorted(multi_set[1:], lambda x, y: len(x) - len(y)) # 當elem 存在於除multi_set[0] 以外的集合時 # 將elem 從result 中刪除 for elem in multi_set[0]: for s in sorted_multi_set: if elem in s: result.remove(elem) break return result
這個算法的複雜度爲O(N2) 執行步數爲S T 其中S 爲輸入集合中基數最小的集合而
T 則爲除第一個集合以外其餘集合的數量。
以Python 代碼表示的算法二定於以下
# coding: utf-8 def sdiff_2(*multi_set): # 用第一個集合做爲結果集的起始值 result = multi_set[0].copy() for s in multi_set[1:]: for elem in s: # 從結果集中刪去其餘集合中包含的元素 if elem in result: result.remove(elem) return result
這個算法的複雜度一樣爲O(N2) 執行步數爲S 其中S 爲全部集合的基數總和。
Redis 使用一個程序決定該使用那個求差集算法程序用Python 表示以下
# coding: utf-8 from sdiff_1 import sdiff_1 from sdiff_2 import sdiff_2 def sdiff(*multi_set): # 算法一的常數項較低給它一點額外的優先級 algo_one_advantage = 2 algo_one_weight = len(multi_set[0]) * len(multi_set[1:]) / algo_one_advantage algo_two_weight = sum(map(len, multi_set)) if algo_one_weight <= algo_two_weight: return sdiff_1(*multi_set) else: return sdiff_2(*multi_set)