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)