Redis-整数集合intset

整数集合使用场景

整数集合(intset)是集合键的底层实现之一: 当一个集合只包含整数值元素, 并且这个集合的元素数量不多时, Redis 就会使用整数集合作为集合键的底层实现。

整数集合(intset)是 Redis 用于保存整数值的集合抽象数据结构, 它可以保存类型为 int16_t 、 int32_t 或者 int64_t 的整数值, 并且保证集合中不会出现重复元素。

整数集合的结构
//每个 intset.h/intset 结构表示一个整数集合:

typedef struct intset {

    // 编码方式
    uint32_t encoding;

    // 集合包含的元素数量
    uint32_t length;

    // 保存元素的数组
    int8_t contents[];

} intset;

在这里插入图片描述

注意:
1.contents 数组是整数集合的底层实现: 整数集合的每个元素都是 contents 数组的一个数组项(item), 各个项在数组中按值的大小从小到大有序地排列, 并且数组中不包含任何重复项。

2.contents 数组的真正类型取决于 encoding 属性的值,对应如下

encodingcontent类型范围
INTSET_ENC_INT16int16_t-32,768 ~ 32,767
INTSET_ENC_INT32int32_t-2,147,483,648 ~ 2,147,483,647
INTSET_ENC_INT64int64_t-9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807
升级

因为 C 语言是静态类型语言, 为了避免类型错误, 我们通常不会将两种不同类型的值放在同一个数据结构里面

每当我们要将一个新元素添加到整数集合里面, 并且新元素的类型比整数集合现有所有元素的类型都要长时, 整数集合需要先进行升级(upgrade), 然后才能将新元素添加到整数集合里面。

降级

intset 不支持降级操作,也就是说 对数组进行了升级后,编码就会一直保持升级后的状态。

源码
/* 创建一个intset */
intset *intsetNew(void) {           //创建一个空集合
    intset *is = zmalloc(sizeof(intset));   //分配空间
    is->encoding = intrev32ifbe(INTSET_ENC_INT16);  //设置编码方式
    is->length = 0; //集合为空
    return is;
}

inset写入逻辑

/* Insert an integer in the intset */
intset *intsetAdd(intset *is, int64_t value, uint8_t *success) {//将value添加到is集合中,如果成功success被设置为1否则为0
    uint8_t valenc = _intsetValueEncoding(value);   //获得value适合的编码类型
    uint32_t pos;
    if (success) *success = 1;  //设置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)) {  //如果value的编码类型大于集合的编码类型,需要进行升级操作
        /* This always succeeds, so we don't need to curry *success. */
        return intsetUpgradeAndAdd(is,value);   //升级集合,并且将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)) {  //查找value,若果value已经存在,intsetSearch返回1,如果不存在,pos保存value可以插入的位置
            if (success) *success = 0;  //value存在,success设置为0
            return is;
        }

        //value在集合中不存在,且pos保存可以插入的位置
        is = intsetResize(is,intrev32ifbe(is->length)+1);   //调整集合大小
        if (pos < intrev32ifbe(is->length)) intsetMoveTail(is,pos,pos+1);   //如果pos不是在数组末尾则要移动调整集合
    }

    _intsetSet(is,pos,value);   //设置pos下标的值为value
    is->length = intrev32ifbe(intrev32ifbe(is->length)+1);  //集合节点数量加1
    return is;
}


/* Upgrades the intset to a larger encoding and inserts the given integer. */
//插入时候需要升级操作并且写入
static intset *intsetUpgradeAndAdd(intset *is, int64_t value) { //根据value的编码方式,对整数集合is的编码格式升级
    uint8_t curenc = intrev32ifbe(is->encoding);    //当前集合的编码方式
    uint8_t newenc = _intsetValueEncoding(value);   //得到value合适的编码方式
    int length = intrev32ifbe(is->length);          //集合元素数量
    int prepend = value < 0 ? 1 : 0;                //如果value小于0,则要将value添加到数组最前端,因此为移动1个编码长度
    //集合的编码格式要升级,也就是内存增大
    //因为 value 的编码比集合原有的其他元素的编码都要大,所以value如果是负数,就是最小值,如果是正数则是最大值
    //索引value要么放在数组集合的最前端,要么最后端,根据prepend判断

    /* First set new encoding and resize */
    is->encoding = intrev32ifbe(newenc);    //更新集合is的编码方式
    is = intsetResize(is,intrev32ifbe(is->length)+1);   //根据新的编码方式重新设置内存空间大小

    /* Upgrade back-to-front so we don't overwrite values.
     * Note that the "prepend" variable is used to make sure we have an empty
     * space at either the beginning or the end of the intset. */
    //_intsetGetEncoded()得到下标为length的值
    //_intsetSet设置下标为prepend+length的值为_intsetGetEncoded返回的值
    //但是,编码格式已经发生改变,数组元素没变但是内存大小改变
    //移动每一个元素的位置从之前旧的位置移动到新的位置
    while(length--)
        _intsetSet(is,length+prepend,_intsetGetEncoded(is,length,curenc));

    /* Set the value at the beginning or the end. */
    if (prepend)    
        _intsetSet(is,0,value); //为负数放在队列的第一个
    else
        _intsetSet(is,intrev32ifbe(is->length),value);  //value为正数,设置最末尾+1的值为value
    is->length = intrev32ifbe(intrev32ifbe(is->length)+1);  //数组元素加1
    return is;
}



参考文件:
http://redisbook.com/preview/intset/upgrade.html
https://github.com/menwengit/redis_source_annotation

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值