Redis系列(二):存储结构

Redis存储结构

1 简介

Redis系列(一):Redis数据结构 可以了解到,Redis为用户提供了多种数据结构及相应API,用户可随心使用,本文将具体介绍这些数据结构的具体实现。

2 Redis存储结构

Redis主要实现了6中存储结构,分别为:整数集合;字典、字符串;链表、压缩列表、跳跃表。

2.1 SDS

SDS是Simple Dynamic String 的首字母缩写,是对C语言中的字符串的封装,其主要数据结构定义如下:

struct sdshdr {
    // 表示已分配使用的字节数
    // buf数组已使用的长度
    int len;
    // 表示SDS未使用的字节数;
    int free;
    // 字节数组保存二进制数据
    // 遵循C语言规则,以'\0'结尾,但不计算在len属性
    char buf[];
};

SDS结构示意图

Redis系列(一):Redis数据结构 已经讲过,Redis所有的键都是字符串类型,也就是说所有的键都是SDS对象,举例来说:

set msg “hello world”
该命令创建了一个键值对,且键和值都是字符串类型,键是一个保存着msg的SDS,值是保存着 hello world的SDS;

SDS是对在C字符串的基础上进行的封装,具有以下优势:

  • 常数复杂度获取字符串长度:C语言获取字符串长度需要进行一次便利,SDS只需要通过获取len属性即可达到目的;
  • 减少修改字符串时带来的内存重分配:C字符串拼接或截断操作都需要重新分配内存空间,而SDS在分配内存空间时,预留了free长度的空闲空间,能够有效减少内存冲分配次数;
  • 二进制安全:C字符串只能保存文本数据,而SDS能保存包括图片、音频、视频等二进制数据,其原因是SDS是通过len属性而不是’\0’来判断字符串的结尾。
  • 杜绝缓冲区溢出:C字符串拼接需要手动分配内存空间,以容纳拼接后的字符串,如果忘记该操作就会导致缓冲区溢出,SDS已经封装好了字符串拼接操作,可完全避免缓冲区溢出问题。

    2.2 链表

    链表是一种常用的数据结构,其基本特性大家都应该很了解,如高效的节点重排能力、顺序性节点访问、灵活的节点增删等。Redis使用的C语言并没有内置这种数据结构,因此Redis自己实现了链表结构。
    链表节点listNode主要包含三个属性:前置节点 *prev、后置节点 *next及节点值 *value,并且通过list结构来持有链表:

/**
 * 链表节点数据结构
 **/
typedef struct listNode{
    struct listNode *prev;
    struct listNode *next;
    void *value;
} listNode;

/**
 * 通过list持有链表
 **/
typedef struct list{
    // 头节点
    listNode *head;
    // 尾节点
    listNode *tail;
    // 链表节点数量
    unsigned int len;
    // 节点复制函数
    void *(*dup)(void *ptr);
    // 节点释放函数
    void *(*free)(void *ptr);
    // 节点值对比函数
    void *(*match)(void *ptr);
} list;

Redis链表具有以下特征:

  • 双端:list结构持有head和tail指针,获取表头或表尾节点时间复杂度为O(1);
  • 双向:链表每个节点都带有prev和next指针,获取前置节点或后置节点时间复杂度为O(1);
  • 无环:头节点的prev和尾节点的next都为NULL;
  • 长度计数器:list通过len属性记录链表节点数量;

    2.3 整数集合

    整数集合是集合键的底层实现之一,当一个集合只包含整数且数量不多时,Redis就会使用整数集合intset作为底层实现。
    intset可以保存int16_t, int32_t, int64_t的整数值,并且可以保证不重复。

typedef struct intset {
    // 编码方式
    uint32_t encoding;
    // 集合包含的元素数量
    uint32_t length;
    // 升序保存着所有元素
    int8_t contents[];
} intset;

contents属性声明为int8_t类型,但实际上元素的类型取决于encoding属性的值:

  • 如果encoding为INTSET_ENC_INT16,那么contents所有元素都是int16_t类型整数值;
  • 如果encoding为INTSET_ENC_INT32,那么contents所有元素都是int32_t类型整数值;
  • 如果encoding为INTSET_ENC_INT64,那么contents所有元素都是int6_4t类型整数值;

    升级: 当将一个新元素添加到整数集合,且其长度比集合中任何元素类型都长时,整数集合需要先进行升级,升级分为三步:
    1)根据新元素类型,分配内存空间,包括已有元素和新元素;
    2)将已有元素转换成新元素类型,并放置到新的内存空间;
    3)将新元素添加到内存空间;
    整个升级过程保证所有元素升序排序。对于具体的内存分配过程,请大家参阅《Redis设计与实现》。
    需要说明的是 整数集合不支持降级,因此一旦对数组进行了升级,编码就会一直保持升级后的状态。

2.4

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值