Redis数据类型

144 阅读3分钟

Redis数据类型

Redis一共有五种数据类类型。

String

String结构底层是一个简单动态字符串,支持扩容,存储字符串。

List

存储线性有序且可重复的元素,底层数据结构可以是双向链表/压缩列表

Set

存储不可重复的元素,一般用于求交集、差集等,底层数据结构可以是hash和整数数组。

Zset

存储的是有序不可重复的元素,Zset为每一个元素添加了一个Score属性作为排序依据,底层数据结构可以是ziplist和跳表

Hash

存储的是键值对,底层数据结构是ziplist和hash

Redis会在性能以及节省内存间考虑,选择最适合当前状态的底层数据结构实现。

Redis动态字符串(Simple Dynamic String——SDS)

Redis中保存的key是字符串,value往往是字符串或者字符串的集合。

Redis没有使用C语言的字符串, 因为

  1. 获取字符串长度需要计算

  2. 非二进制安全

  3. 不可修改

源码

struct __atrribute__ ((__packed__)) sdshdr8 {
    uint8_t len;  // buf已保存的字符串字节数
    uint8_t alloc; // buf申请的总的字节数,不包含结束标志
    unsigned char flags; // 不同SDS的头类型,用来控制SDS的头大小
    char buf[];
}

动态扩容

假设现在有一个内容为“hi”的SDS,要给SDS追加一段字符串“,Amy”,这里首先会申请新内存空间:

  1. 如果新字符串小于1M, 则新空间为扩展后字符串长度的两倍+1(因为结束标识符'\0',所以加1)

  2. 如果新字符串大于1M,则新空间为扩展后字符串长度+1M+1, 称为内存预分配

优点

  1. 获取字符串长度的时间复杂度为O(1)

  2. 支持动态扩容

  3. 减少内存分配次数

  4. 二进制安全

IntSet

IntSet是Redis中set集合的一种实现方式,基于整数数组来实现,并且具备长度可变、有序等特征。

typedef struct intset {
    uint32_t encoding; // 编码方式,支持存放16位,32位和64位整数
    uint32_t length; // 元素个数
    int8_t contents[]; // 整数数组, 保存集合数据
} intset;

encoding的三个模式

  1. 2字节整数,范围类似java的short

  2. 4字节整数,范围类似java的int

  3. 8字节整数,范围类似java的long

为了方便查找,Redis会将intset中的所有整数按照升序依次保存在contents数组中

inset的升级

假设有一个intset, 元素为{5, 10, 20}, 采用的编码是INSET_ENC_INT16,则每个整数占2字节。

向其中添加一个数字,50000。这个数字超过了int16_t的范围,intset会自动升级编码方式到合适的大小。

流程如下:

  1. 升级编码到INSET_ENC_INT32, 每个整数占4字节,并按照新的编码方式及元素个数扩容数组

  2. 倒序依次将数组中的元素拷贝到扩容后的正确位置(倒序的原因是防止数据被覆盖

  3. 将待添加的元素放入数组末尾

  4. 最后,将intset的encoding属性改为INTSET_ENC_INT32,将length属性改为4