Redis源码学习 --> 基本数据结构 --> Intset
1. 什么是Intset?
Intset顾名思义就是整数集合。是Set数据类型的底层结构之一。
2. 为什么用Intset?
核心目的是节省空间,且只能在元素数量少的情况使用。
3. Intset结构分析
Intset的结构很简单,代码如下:
typedef struct intset {
uint32_t encoding; // 编码方式, 即元素占用的字节数(只能取2、4、8)
uint32_t length; // 元素个数
int8_t contents[]; // 有符号整数 数组,类型取决于encoding
} intset;
contents内的元素是同一类型,实际只有{INT16、INT32、INT64}三种。元素按从小到大排列,方便二分法的查找元素。
结构图如下:

编码时尽可能选择最小的一种。
- 当新增元素时,如果元素值超过类型的上限值,则编码升级。(如: INT16 编码插入了1000000超过 INT16_MAX)
- 当删除元素时,任何情况不进行降级,防止编码反复切换。
4. 增删查的操作代码
查询操作:
static uint8_t intsetSearch(intset *is, int64_t value, uint32_t *pos) {
int min = 0, max = intrev32ifbe(is->length)-1, mid = -1;
int64_t cur = -1;
/* The value can never be found when the set is empty */
if (intrev32ifbe(is->length) == 0) {
if (pos) *pos = 0;
return 0;
} else {
/* Check for the case where we know we cannot find the value,
* but do know the insert position. */
if (value > _intsetGet(is,max)) {
if (pos) *pos = intrev32ifbe(is->length);
return 0;
} else if (value < _intsetGet(is,0)) {
if (pos) *pos = 0;
return 0;
}
}
/* 典型的二分法查找 */
while(max >= min) {
mid = ((unsigned int)min + (unsigned int)max) >> 1;
cur = _intsetGet(is,mid);
if (value > cur) {
min = mid+1;
} else if (value < cur) {
max = mid-1;
} else {
break;
}
}
if (value == cur) {
if (pos) *pos = mid;
return 1;
} else {
if (pos) *pos = min;
return 0;
}
}
增加操作:
intset *intsetAdd(intset *is, int64_t value, uint8_t *success) {
uint8_t valenc = _intsetValueEncoding(value);
uint32_t pos;
if (success) *success = 1;
if (valenc > intrev32ifbe(is->encoding)) {
/* 需要升级则重新分配空间, 拷贝数据, 并将新值添加到末尾 */
return intsetUpgradeAndAdd(is,value);
} else {
/* 查找元素, 如果存在则返回失败 */
if (intsetSearch(is,value,&pos)) {
if (success) *success = 0;
return is;
}
/* 扩容放新元素, 将插入位置后面的元素memmove()右移一个数 */
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;
}
删除操作:
intset *intsetRemove(intset *is, int64_t value, int *success) {
uint8_t valenc = _intsetValueEncoding(value);
uint32_t pos;
if (success) *success = 0;
/* 判断待删除的元素存在 */
if (valenc <= intrev32ifbe(is->encoding) && intsetSearch(is,value,&pos)) {
uint32_t len = intrev32ifbe(is->length);
if (success) *success = 1;
/* 将删除的元素后的所有元素memmove()左移一个数 */
if (pos < (len-1)) intsetMoveTail(is,pos+1,pos);
is = intsetResize(is,len-1); // 缩容节省空间
is->length = intrev32ifbe(len-1);
}
return is;
}

浙公网安备 33010602011771号