概述

集合Set, 集合成员是唯一的,这就意味着集合中不能出现重复的数据。

数据结构

Set的实现与Java的实现相似最终都是使用哈希表来存储,key为要存储的值,value为Null。

  • 如果能够转成int的对象(isObjectRepresentableAsLongLong),那么就用intset保存。
  • 如果用intset保存的时候,如果长度超过512(REDIS_SET_MAX_INTSET_ENTRIES)就转为hashtable编码。
  • 其他情况统一用hashtable进行存储
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
int setTypeAdd(robj *subject, sds value) {
long long llval;
// 字典
if (subject->encoding == OBJ_ENCODING_HT) {
dict *ht = subject->ptr;
// value为字典的key,字典的value为null
dictEntry *de = dictAddRaw(ht,value,NULL);
if (de) {
dictSetKey(ht,de,sdsdup(value));
dictSetVal(ht,de,NULL);
return 1;
}
} else if (subject->encoding == OBJ_ENCODING_INTSET) {
// 可以作为整数保存
if (isSdsRepresentableAsLongLong(value,&llval) == C_OK) {
uint8_t success = 0;
subject->ptr = intsetAdd(subject->ptr,llval,&success);
if (success) {
/* Convert to regular set when the intset contains
* too many entries. */
if (intsetLen(subject->ptr) > server.set_max_intset_entries)
setTypeConvert(subject,OBJ_ENCODING_HT);
return 1;
}
// 如果对象的值不能编码为整数,那么将集合从 intset 编码转换为 HT 编码
// 然后再执行添加操作
} else {
/* Failed to get integer from object, convert to regular set. */
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;
}

intset 整型集合

有3个属性:

  1. 编码
  2. 数据长度
  3. 数据内容
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    typedef struct intset {
    uint32_t encoding;
    uint32_t length;
    int8_t contents[];
    } intset;
    ```

    ### 插入 intsetAdd

    ```c
    intset *intsetAdd(intset *is, int64_t value, uint8_t *success) {
    uint8_t valenc = _intsetValueEncoding(value);
    uint32_t pos;
    if (success) *success = 1;

    /* Upgrade encoding if necessary. If we need to upgrade, we know that
    * this value should be either appended (if > 0) or prepended (if < 0),
    * because it lies outside the range of existing values. */
    if (valenc > intrev32ifbe(is->encoding)) {
    /* This always succeeds, so we don't need to curry *success. */
    return intsetUpgradeAndAdd(is,value);
    } else {
    /* Abort if the value is already present in the set.
    * This call will populate "pos" with the right position to insert
    * the value when it cannot be found. */
    if (intsetSearch(is,value,&pos)) {
    if (success) *success = 0;
    return is;
    }

    is = intsetResize(is,intrev32ifbe(is->length)+1);
    if (pos < intrev32ifbe(is->length)) intsetMoveTail(is,pos,pos+1);
    }

    _intsetSet(is,pos,value);
    is->length = intrev32ifbe(intrev32ifbe(is->length)+1);
    return is;
    }
    /* Set the value at pos, using the configured encoding. */
    static void _intsetSet(intset *is, int pos, int64_t value) {
    uint32_t encoding = intrev32ifbe(is->encoding);

    if (encoding == INTSET_ENC_INT64) {
    ((int64_t*)is->contents)[pos] = value;
    memrev64ifbe(((int64_t*)is->contents)+pos);
    } else if (encoding == INTSET_ENC_INT32) {
    ((int32_t*)is->contents)[pos] = value;
    memrev32ifbe(((int32_t*)is->contents)+pos);
    } else {
    ((int16_t*)is->contents)[pos] = value;
    memrev16ifbe(((int16_t*)is->contents)+pos);
    }
    }