目录
Set 类型是一个无序并唯一的键值集合,它的存储顺序不会按照插入的先后顺序进行存储。一个集合最多可以存储 2^32-1
个元素。
Set 类型除了支持集合内的增删改查,同时还支持多个集合取交集、并集、差集。
Set 的差集、并集和交集的计算复杂度较高,在数据量较大的情况下,如果直接执行这些计算,会导致 Redis 实例阻塞。可以选择一个从库完成聚合统计,或者把数据返回给客户端,由客户端来完成聚合统计。
Set 类型和 List 类型的区别如下:
- List 可以存储重复元素,Set 只能存储非重复元素;
- List 是按照元素的先后顺序存储元素的,而 Set 则是无序方式存储元素的。
Set内部实现
Set 类型的底层数据结构是由哈希表Dict或整数集合IntSet实现的:
- 如果集合中的元素都是整数且元素个数小于
512
(默认值,set-maxintset-entries
配置)个,Redis 会使用整数集合作为 Set 类型的底层数据结构,以节省空间;- 如果集合中的元素不满足上面条件,则 Redis 使用哈希表作为 Set 类型的底层数据结构,Dict中的key用来存储元素,value统一为null。
源码片段
Set存储数据片段
Intset转换成Dict存储片段
int setTypeAdd(robj *subject, sds value) {
long long llval;
//已经是哈希编码,直接添加元素
if (subject->encoding == OBJ_ENCODING_HT) {
dict *ht = subject->ptr;
dictEntry *de = dictAddRaw(ht,value,NULL);
if (de) {
dictSetKey(ht,de,sdsdup(value));
dictSetVal(ht,de,NULL);
return 1;
}
}
//目前是IntSet
else if (subject->encoding == OBJ_ENCODING_INTSET) {
//判断value是否是整数
if (isSdsRepresentableAsLongLong(value,&llval) == C_OK) {
uint8_t success = 0; //是整数,直接添加元素到set
subject->ptr = intsetAdd(subject->ptr,llval,&success);
if (success) {
/* 当intset元素超出set_max_intset_entries,则转为哈希*/
if (intsetLen(subject->ptr) > server.set_max_intset_entries)
setTypeConvert(subject,OBJ_ENCODING_HT);
return 1;
}
}
else {
/*不是整数,直接转为哈希*/
setTypeConvert(subject,OBJ_ENCODING_HT);
/* The set *was* an intset and this value is not integer
* encodable, so dictAdd should always work. */
serverAssert(dictAdd(subject->ptr,sdsdup(value),NULL) == DICT_OK);
return 1;
}
}
else {
serverPanic("Unknown set encoding");
}
return 0;
}