redis中的整数集合

近来在看黄健宏的《Redis设计与实现》,读到整数集合这一节,其中有一些细节问题没有读到。

在互联网上也没有搜到答案,于是看源码有所得,于此记录一下同时也作为分享。

《Redis设计与实现》第一版发布于2014年4月,所用的redis版本是2.9, 比较旧,有些内容与最新版本也有所不同,下文也有提及。

对于书中原文提到的内容可以参考此博客: www.cnblogs.com/panxianhao/..., 与书中分毫不差。

同时在这里感谢黄先生对Redis源码的分享。


整数集合 (intset)是集合键的底层实现之一,当一个集合只包含整数值元素,并且这个集合的元素数量不多时,Redis 就会使用整数集合作为集合键的底层实现。 举个例子,如果我们创建一个只包含五个元素的集合键,并且集合中的所有元素都是整数值,那么这个集合键的底层实现就会是整数集合

书中原文是这样提的,但是下载Redis源码之后,发现在2023年11月30日下载的redis-7.2.3中,intset中可存储数据的大小限制变为1GB:

c 复制代码
// src/t_set.c
/* Return the maximum number of entries to store in an intset. */
static size_t intsetMaxEntries(void) {
    size_t max_entries = server.set_max_intset_entries;
    /* limit to 1G entries due to intset internals. */
    if (max_entries >= 1<<30) max_entries = 1<<30;
    return max_entries;
}

并且在数据过多,超过上限时转换为HT(hashTable)

c 复制代码
src/t_set.c
/* Converts intset to HT if it contains too many entries. */
static void maybeConvertIntset(robj *subject) {
    serverAssert(subject->encoding == OBJ_ENCODING_INTSET);
    if (intsetLen(subject->ptr) > intsetMaxEntries())
        setTypeConvert(subject,OBJ_ENCODING_HT);
}

整数集合的数据结构:

c++ 复制代码
typedef struct intset {
    uint32_t encoding;
    uint32_t length;
    int8_t contents[];
} intset;

encoding表示contents中存储数据的类型,有如下定义:

c 复制代码
/* Note that these encodings are ordered, so:
 * INTSET_ENC_INT16 < INTSET_ENC_INT32 < INTSET_ENC_INT64. */
#define INTSET_ENC_INT16 (sizeof(int16_t))
#define INTSET_ENC_INT32 (sizeof(int32_t))
#define INTSET_ENC_INT64 (sizeof(int64_t))

contents虽然类型是int8_t,但不会存储int8_t的数据,相当于是字节组成的数组吧 具体赋值插入时的算法如下,会把这个数组转换为具体的类型

c 复制代码
//intset.c
/* 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);
    }
}

升级与扩容:

当新加入的数据的类型占据的内存大小比之前的大时,会升级contents中元素的类型 比如原来有3个int16_t的数据,现在添加了一个int32类型的值,那就会把整个数组中的元素升级为int32_t,然后再插入

扩容原文没有提, 查看源码如下:

c 复制代码
// intset.c
is = intsetResize(is,intrev32ifbe(is->length)+1);
if (pos < intrev32ifbe(is->length)) intsetMoveTail(is,pos,pos+1);

/* Resize the intset */
static intset *intsetResize(intset *is, uint32_t len) {
    uint64_t size = (uint64_t)len*intrev32ifbe(is->encoding);
    assert(size <= SIZE_MAX - sizeof(intset));
    is = zrealloc(is,sizeof(intset)+size);
    return is;
}

每次只新增一个元素大小的空间,然后把原来的数据进行移动(根据新插入元素的pos)

注意: intset不支持降级

添加与删除元素的时间复杂度:

都是O(N), 因为要移动元素,再调整空间大小

c 复制代码
//intset.c
/* Delete integer from intset */
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);

        /* We know we can delete */
        if (success) *success = 1;

        /* Overwrite value with tail and update length */
        if (pos < (len-1)) intsetMoveTail(is,pos+1,pos);
        is = intsetResize(is,len-1);
        is->length = intrev32ifbe(len-1);
    }
    return is;
}

书中的重点回顾此作为记录:

  • 整数集合是集合键的底层实现之一
  • 以有序、无重复的方式保存集合元素,在有需要时,程序会根据新添加元素的类型,改变这个数组的类型
  • 升级操作为整数集合带来了操作上的灵活性,并且尽可能地节约了内存
  • 整数集合只支持升级操作,不支持降级操作
相关推荐
qq_51583806 彩雷王33 分钟前
1004-05,使用workflow对象创建http任务,redis任务
redis·网络协议·http
Wang's Blog1 小时前
Redis: Sentinel节点管理,故障迁移一致性以及TILT模式
redis·sentinel
九圣残炎2 小时前
【springboot】简易模块化开发项目整合Redis
spring boot·redis·后端
小登ai学习3 小时前
简单认识 redis -3 -其他命令
数据库·redis·缓存
BergerLee16 小时前
对不经常变动的数据集合添加Redis缓存
数据库·redis·缓存
huapiaoy16 小时前
Redis中数据类型的使用(hash和list)
redis·算法·哈希算法
【D'accumulation】17 小时前
令牌主动失效机制范例(利用redis)注释分析
java·spring boot·redis·后端
Cikiss17 小时前
微服务实战——SpringCache 整合 Redis
java·redis·后端·微服务
一休哥助手18 小时前
Redis 五种数据类型及底层数据结构详解
数据结构·数据库·redis
盒马盒马19 小时前
Redis:zset类型
数据库·redis