深入Redis底层数据结构,看这篇文章就够了

前言

Redis的value的常见几种数据类型,有string字符串类型、list列表类型、set集合类型、sortedset(zset)有序集合类型、hash类型等。本篇我们来深入研究下这些数据结构的底层实现,从而对Redis有更深层次的认识。

Redis底层数据结构

image-20210313150113381

Redis没有表的概念,Redis实例所对应的db以编号区分,db本身就是key的命名空间。
比如:user:1000作为key值,表示在user这个命名空间下id为1000的元素,类似于user表的id=1000的
行。

对于常见的几种数据类型,分别对应的底层实现如下图:

image-20210707203909451

对于每种数据类型,redis都有不止一种的实现方式,目的是为了提高效率:

  • 连续内存空间 - 数据量少时,节省空间。(占用空间小于某个阈值(默认512kb),元素个数少于某个阈值(默认64个))
  • 正常实现 - 数据量大时。

RedisDB结构

Redis中存在“数据库”的概念,该结构由redis.h中的redisDb定义。
当redis 服务器初始化时,会预先分配 16 个数据库
所有数据库保存到结构 redisServer 的一个成员 redisServer.db 数组中
redisClient中存在一个名叫db的指针指向当前使用的数据库
RedisDB结构体源码:

typedef struct redisDb {
   
    int id;     //id是数据库序号,为0-15(默认Redis有16个数据库)
    long avg_ttl;    //存储的数据库对象的平均ttl(time to live),用于统计
    dict *dict;    //存储数据库所有的key-value
    dict *expires;   //存储key的过期时间
    dict *blocking_keys;    //blpop 存储阻塞key和客户端对象
    dict *ready_keys;    //阻塞后push 响应阻塞客户端 存储阻塞后push的key和客户端对象
    dict *watched_keys;    //存储watch监控的的key和客户端对象
} redisDb;

RedisObject结构

Redis中value是一个对象,里面包含了字符串对象、列表对象、哈希对象、集合对象和有序集合对象等。

RedisObject的结构体信息如下:

typedef struct redisObject {
   
    unsigned type:4;//类型 五种对象类型
    unsigned encoding:4;//编码
    void *ptr;//指向底层实现数据结构的指针
    //...
    int refcount;//引用计数
    //...
    unsigned lru:LRU_BITS; //LRU_BITS为24bit 记录最后一次被命令程序访问的时间
    //...
}robj;

下面我们对其中的变量一一进行介绍:

type

type表示对象的类型,占4位

取值主要有如下几种类型:

REDIS_STRING(字符串)、REDIS_LIST (列表)、REDIS_HASH(哈希)、REDIS_SET(集合)、REDIS_ZSET(有
序集合)。

当我们执行 type 命令时,便是通过读取 RedisObject 的 type 字段获得对象的类型 ,如下所示:

127.0.0.1:6379> keys *
1) "hit:1"
2) "user:001"
3) "age"
4) "set:1"
5) "list:1"
127.0.0.1:6379> type age
string
127.0.0.1:6379> type hit:1
zset

字符串对象

C语言: 字符数组 (以"\0" 结尾)

Redis 使用了 SDS(Simple Dynamic String),用于存储字符串和整型数据。

结构体代码:

struct sdshdr{
   
//记录buf数组中已使用字节的数量
int len;
//记录 buf 数组中未使用字节的数量
int free;
//字符数组,用于保存字符串
char buf[];
}

示意图如下:

image-20210313160103869

buf[] 的长度=len+free+1

sds在Redis中是实现字符串对象的工具,并且完全取代char*…sds是二进制安全的,它可以存储任意二进制数据,不像C语言字符串那样以‘\0’来标识字符串结束,因为传统C字符串符合ASCII编码,这种编码的操作的特点就是:遇零则止 。即,当读一个字符串时,只要遇到’\0’结尾,就认为到达末尾,就忽略’\0’结尾以后的所有字符。因此,如果传统字符串保存图片,视频等二进制文件,操作文件时就被截断了。

SDS表头的buf被定义为字节数组,因为判断是否到达字符串结尾的依据则是表头的len成员,这意味着它可以存放任何二进制的数据和文本数据,包括’\0’.

SDS 和传统的 C 字符串获得的做法不同,传统的C字符串遍历字符串的长度,遇零则止,复杂度为O(n)。而SDS表头的len成员就保存着字符串长度,所以获得字符串长度的操作复杂度为O(1)

SDS的优势:

1、SDS 在 C 字符串的基础上加入了 free 和 len 字段,获取字符串长度:SDS 是 O(1),C 字符串是
O(n)。

2、 SDS 由于记录了长度,在可能造成缓冲区溢出时会自动重新分配内存,杜绝了缓冲区溢出。

3、可以存取二进制数据,以字符串长度len来作为结束标识

C字符串: \0作为字符串结束的标志,由于二进制数据包括空字符串,所以没有办法存取二进制数据

SDS : 非二进制数据用\0来标识结束,二进制数据用字符串长度来标识,所以可以存二进制数据

总结下SDS的特点是:可动态扩展内存二进制安全快速遍历字符串与传统的C语言字符串类型兼容

SDS的应用场景:存储字符串和整型数据、存储key、AOF缓冲区和用户输入缓冲。

跳跃表

跳跃表是有序集合(sorted-set)的底层实现,效率高,实现简单。

跳跃表的基本思想:将有序链表中的部分节点分层,每一层都是一个有序链表。

查找功能举例:

思路:在查找时优先从最高层开始向后查找,当到达某个节点时,如果next节点值大于要查找的值或next指针
指向null,则从当前节点下降一层继续向后查找。

image-20210313161404050

比如我们要查找元素9,按道理我们需要从头结点开始遍历,一共遍历8个结点才能找到元素9。

我们对这个链表进行分层,如下:

image-20210313161554386

此时我们只需遍历5次找到元素9(红色的线为查找路径)

沿着这个思路,我们继续进行第二次分层:

image-20210313161653510

我们只需遍历4次便能找到元素9

继续第三次分层:

image-20210313161732757

可以发现,此时我们还是需要遍历4次才能找到元素9,跟第二次分层的效果一样。

像上面这种数据结构,就是跳跃表,和二分查找有

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Redis是一个开源的内存数据库,它使用了多种数据结构来存储不同类型的数据。下面是几种常见的Redis底层数据结构的详解: 1. 字符串(String):字符串是Redis中最基本的数据结构。它可以存储任意类型的数据,包括数字、文本等。字符串在Redis中以字节数组的形式存储,可以通过键访问和修改。 2. 列表(List):列表是一个有序的字符串集合,可以在列表的两端进行插入、删除和获取操作。Redis使用双向链表来实现列表数据结构,它支持快速插入和删除操作。 3. 哈希(Hash):哈希是一种键值对的集合。在Redis中,哈希可以存储多个字段和对应的值,类似于关联数组或者字典。哈希在内部使用哈希表来实现,可以快速查找和修改字段值。 4. 集合(Set):集合是一组唯一且无序的字符串集合。Redis使用哈希表来实现集合数据结构,它支持添加、删除和判断元素是否存在等操作。 5. 有序集合(Sorted Set):有序集合是一组唯一且有序的字符串集合。在Redis中,每个元素都会关联一个分数,通过分数可以对元素进行排序。有序集合的实现使用了跳跃表和哈希表两种数据结构,它支持添加、删除、修改和范围查询等操作。 这些数据结构底层实现都是高效的,并且支持丰富的操作。Redis数据结构灵活性较高,能够满足不同类型的数据存储需求。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员资料站

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值