redis 整数集合

Redis中的整数集合是一种节约内存的集合实现方式,其特点包括根据encoding动态调整数据宽度,不预分配空间,每次添加元素会重新计算encoding并可能重新分配内存。整数集合在编码和结构上保证了灵活性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

整数集合是redis集合的实现方式之一
主要好处是灵活、节约内存空间

整数集合类定义

集合种存储的数据宽度是根据encoding来定义的,如果等于INTSET_ENC_INT16数据宽度为16位,INTSET_ENC_INT32是32位,INTSET_ENC_INT64是32位

typedef struct intset {
	//编码方式
	//主要有3种编码格式:INTSET_ENC_INT16、INTSET_ENC_INT32、INTSET_ENC_INT64
    uint32_t encoding;

	//集合中的数量
    uint32_t length;

	//保存的值
    int8_t contents[];
} intset;

整数集合结构图

如下图所示:
encoding = INSET_ENC_INT16标示contents中存储的数据宽度为16位,length=5 标示当前集合中有5个元素
contents存储具体的数据
在这里插入图片描述

整数集合的特点

  • 整数集合不会预分配空间,每次添加数据,都会重新计算encoding,重新分配空间
  • 整数集合不会降级

代码分析

可以从以下代码看出,不论encoding是否变化,只要插入元素就会重新分配内存

//向整数集合中插入数据
intset *intsetAdd(intset *is, int64_t value, uint8_t *success) {
	//计算新增值的encoding
    uint8_t valenc = _intsetValueEncoding(value);
    uint32_t pos;
    if (success) *success = 1;

    //如果新增的值的encoding大于当前整数集合的encoding
    if (valenc > intrev32ifbe(is->encoding)) {
        return intsetUpgradeAndAdd(is,value);
    } else {
        //返回当前值应该插入的位置,如果已存在直接返回
        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;
}

//升级encoding并插入value
static intset *intsetUpgradeAndAdd(intset *is, int64_t value) {
    uint8_t curenc = intrev32ifbe(is->encoding);
    uint8_t newenc = _intsetValueEncoding(value);
    int length = intrev32ifbe(is->length);
    int prepend = value < 0 ? 1 : 0;

    //设置新的encoding
    is->encoding = intrev32ifbe(newenc);
	//重新分配空间
    is = intsetResize(is,intrev32ifbe(is->length)+1);
	
	//因为是upgrade,所以新增的数据只可能在两端,如果prepend=1则预留第一个位置,否则预留最后一个位置
    while(length--)
        _intsetSet(is,length+prepend,_intsetGetEncoded(is,length,curenc));

    //设置元素值
    if (prepend)
        _intsetSet(is,0,value);
    else
        _intsetSet(is,intrev32ifbe(is->length),value);

	//设置整数集合长度
    is->length = intrev32ifbe(intrev32ifbe(is->length)+1);
    return is;
}

//重新分配整数集合的大小
static intset *intsetResize(intset *is, uint32_t len) {
	//计算元素大小
    uint32_t size = len*intrev32ifbe(is->encoding);
    is = zrealloc(is,sizeof(intset)+size);
    return is;
}

void *zrealloc(void *ptr, size_t size) {
#ifndef HAVE_MALLOC_SIZE
    void *realptr;
#endif
    size_t oldsize;
    void *newptr;

    if (size == 0 && ptr != NULL) {
        zfree(ptr);
        return NULL;
    }
    if (ptr == NULL) return zmalloc(size);
#ifdef HAVE_MALLOC_SIZE
    oldsize = zmalloc_size(ptr);
    newptr = realloc(ptr,size);
    if (!newptr) zmalloc_oom_handler(size);

    update_zmalloc_stat_free(oldsize);
    update_zmalloc_stat_alloc(zmalloc_size(newptr));
    return newptr;
#else
    realptr = (char*)ptr-PREFIX_SIZE;
    oldsize = *((size_t*)realptr);
    newptr = realloc(realptr,size+PREFIX_SIZE);
    if (!newptr) zmalloc_oom_handler(size);

    *((size_t*)newptr) = size;
    update_zmalloc_stat_free(oldsize+PREFIX_SIZE);
    update_zmalloc_stat_alloc(size+PREFIX_SIZE);
    return (char*)newptr+PREFIX_SIZE;
#endif
}
### Redis 支持的五种数据结构及其用法 #### 1. **String(字符串类型)** `String` 是一种简单的键值对储形式,其值可以是字符串或者整数。尽管 `Redis` 使用的是 C 语言开发,但它并未采用标准的 C 字符串表示方式,而是设计了自己的简单动态字符串(Simple Dynamic String, SDS),这种实现允许它不仅能够保普通的文本数据,还支持二进制数据[^2]。 - 获取字符串长度的时间复杂度为 \(O(1)\),而传统的 C 字符串则需要遍历整个字符串来计算长度,时间复杂度为 \(O(N)\)。 - 常见操作包括设置值 (`SET`) 和获取值 (`GET`)。 ```bash SET mykey "Hello" GET mykey ``` --- #### 2. **Hash(哈希类型)** `Hash` 类型是一种字段和值之间的映射关系集合,适合用于储对象属性。它的底层是一个字典结构,因此查找效率非常高。 - 主要用途:当需要将多个字段组合在一起时,比如用户的个人信息。 - 常用命令有 `HSET`, `HGETALL`, `HMSET` 等。 ```bash HSET user:1000 name "Alice" age 30 city "New York" HGETALL user:1000 ``` --- #### 3. **List(列表类型)** `List` 是一个有序的字符串列表,基于双向链表实现。它可以用来模拟队列或栈的行为。 - 插入和删除操作主要发生在两端,即头部和尾部,这些操作的时间复杂度均为 \(O(1)\)。 - 可以通过 `LPUSH` 向左端插入元素,通过 `RPOP` 删除右端元素。 ```bash LPUSH list_key value1 value2 LRANGE list_key 0 -1 ``` --- #### 4. **Set(集合类型)** `Set` 表示一组无序且唯一的字符串成员,内部由哈希表实现,从而保证了添加、删除和查询的操作具有常量级的时间复杂度 \(O(1)\)[^3]。 - 集合非常适合处理去重需求,例如记录访问过某个网站的不同 IP 地址。 - 关联命令如 `SADD`, `SMEMBERS`, `SINTER` 等可用于管理集合内的成员。 ```bash SADD unique_users user1 user2 user3 SMEMBERS unique_users ``` --- #### 5. **Sorted Set(有序集合类型)** `Sorted Set` 结合了 `Set` 的特性和分数排序功能,其中每个成员都有一个关联的分值,按照该分值从小到大排列。 - 成员唯一性仍然保持不变,如果尝试向同一个成员赋不同的分值,则只会更新原有分值而不增加新成员。 - 应用场景广泛,例如排行榜系统可以根据得分实时调整排名顺序。 ```bash ZADD leaderboard 100 playerA 200 playerB 150 playerC ZRANGEBYSCORE leaderboard 0 200 WITHSCORES ``` --- ### 总结 上述五类数据结构覆盖了大部分实际应用中的需求,开发者应根据具体业务逻辑选择合适的类型并充分利用它们的特点优化性能表现[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值