深入了解Redis中Set底层结构

749 阅读3分钟
  • 存储类型与操作命令
类型(Type key)存储编码(OBJECT encoding key)实例命令
setinsetsadd myset1 11 23 54 66
sethashtablesadd myset a b c d e f

问题:Redis是如何决定存储inset还是hashtable?

答:在Redis配置文件有一个叫set-max-inset-entries的属性(默认512)其意义是inset集合中的元素都是整数且元素个数小于set-max-inset-entries配置(默认512)个数,redis会选用它来减少内存的使用,若无法满足intset条件时(如实际超过512个)hashtable作为内部实现

存储原理

  • inset
typedef struct intset {
  uint32_t encoding;//编码类型 int16_t int32_t int64_t
  uint32_t length;//元素个数 最大长度:2的32次幂
  int8_t contents[];//柔性数组,根据encoding字段决定几个字节表示一个元素
} intset
  • 结构示意图

Intset的结构.png

  • encoding

encoding:编码类型,决定每个元素占用几个字节。有如下3种类型。

1.INTSET_ENC_INT16:当元素值都位于INT16_MIN和INT16_MAX之间时使用。 该编码方式为每个元素占用2个字节。

2.INTSET_ENC_INT32:当元素值位于INT16_MAX到INT32_MAX或者INT32_MIN到INT16_MIN之间时使用。该编码方式为每个元素占用4个字节。

3.INTSET_ENC_INT64:当元素值位于INT32_MAX到INT64_MAX或者INT64_MIN到INT32_MIN之间时使用。该编码方式为每个元素占用8个字节

判断一个值需要什么类型的编码格式,只需要查看该值所处的范围即可:

编码
INTSET_ENC_INT16[-32768,32767]
INTSET_ENC_INT32(32767,2147483647)或[-2147483648,-32768]
INTSET_ENC_INT64(2147483647,9223372036854775807)或[-9223372036854775808,-2147483648]

根据encoding属性扩容:

intset结构体会根据待插入的值决定是否需要进行扩容操作。扩容会修改encoding字 段,而encoding字段决定了一个元素在contents柔性数组中占用几个字节。所以当修改encoding字段之后,intset中原来的元素也需要在contents中进行相应的扩展,只要待插入的值导致了扩容,则该值在待插入的intset中不 是最大值就是最小值

//encoding字段在Redis中使用宏来表示,其定义如下:

define INTSET_ENC_INT16 (sizeof(int16_t))
define INTSET_ENC_INT32 (sizeof(int32_t))
define INTSET_ENC_INT64 (sizeof(int64_t))

​ 编码类型

INTSET_ENC_INT162
INTSET_ENC_INT324
INTSET_ENC_INT648

因为encoding字段实际取值为2、4、8,所以encoding字段可以直接比较大小。当待插 入值的encoding字段大于待插入intset的encoding时,说明需要进行扩容操作,并且也能表 明该待插入值在该intset中肯定不存在。

应用场景

  • 抽奖
  • 点赞、签到、打卡(用户记录)
  • 商品标签、用户画像(人口属性、信用属性、社交、兴趣爱好····)
  • 用户关注、推荐模型