Set详解

[toc]

Set是Redis中的单列集合,具有以下特点:

  • 无序
  • 元素唯一
  • 支持交集、差集、并集

Set的底层是哈希表,也就是Dict数据结构。

  • Dict中的key用来存储元素,value一律设为null(Redis6)
    • Redis7中,若元素数量不多,使用listpack
  • 当存储的所有数据都是整数,且元素数量不超过set-max-intset-entries时,Set会采用intset编码,节省内存。
    • 当插入元素时Redis会进行检查,如果不满足条件,编码将转换为HT(listpack / intset -> HT)
示例:

以下是基于Redis6的示例图。

空Set插入三个整数5、10、20。由于插入的都是整数,且数量较少,因此采用intset编码。

image-20240922143114798

之后插入了字符串,不满足条件,编码由intset转为HT。

image-20240922143318545

源码
  • Redis6:
1
2
3
4
5
6
// t_set.c
robj *setTypeCreate(sds value) {
if (isSdsRepresentableAsLongLong(value,NULL) == C_OK) // 都是整数且满足条件,使用intset
return createIntsetObject();
return createSetObject(); // 否则使用HT
}
  • Redis7:
1
2
3
4
5
6
7
8
9
10
11
12
13
// t_set.c
robj *setTypeCreate(sds value, size_t size_hint) {
if (isSdsRepresentableAsLongLong(value,NULL) == C_OK && size_hint <= server.set_max_intset_entries)
return createIntsetObject(); // 都是整数且满足条件,使用intset
if (size_hint <= server.set_max_listpack_entries) // 否则使用listpack或dict
return createSetListpackObject();

/* We may oversize the set by using the hint if the hint is not accurate,
* but we will assume this is acceptable to maximize performance. */
robj *o = createSetObject();
dictExpand(o->ptr, size_hint);
return o;
}
1
2
3
4
5
6
7
8
// t_set.c
void setTypeMaybeConvert(robj *set, size_t size_hint) {
if ((set->encoding == OBJ_ENCODING_LISTPACK && size_hint > server.set_max_listpack_entries)
|| (set->encoding == OBJ_ENCODING_INTSET && size_hint > server.set_max_intset_entries))
{
setTypeConvertAndExpand(set, OBJ_ENCODING_HT, size_hint, 1);
}
}