Redisbook學習筆記(3)數據類型之集合

REDIS_SET 集合 是SADD 、SRANDMEMBER 等命令的操做對象 它使用算法

REDIS_ENCODING_INTSET 和REDIS_ENCODING_HT 兩種方式編碼數據結構

wKioL1MMhAygab3HAACc0PuXNzw010.jpg

編碼的選擇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

wKiom1MMhI-xzeHdAAEfGjLBdwo023.jpg

集合命令的實現

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)
相關文章
相關標籤/搜索