Redis—底层数据结构

存储系统结构:

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;

id
数据库序号,为0-15(默认Redis有16个数据库)

dict
存储数据库所有的key-value

expires

存储key的过期时间
 

RedisObject结构

Value是一个对象,包含字符串对象,列表对象,哈希对象,集合对象和有序集合对象常用的五种数据结构。

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

表示对象的类型,占 4 位;常用REDIS_STRING(字符串)、REDIS_LIST (列表)、REDIS_HASH(哈希)、REDIS_SET(集合)、REDIS_ZSET(有序集合)。

  • encoding

对象的内部编码,占 4 位;对于redis支持的每种类型都至少有两种编码。

Redis 可以根据不同的使用场景来为对象设置不同的编码,大大提高了 Redis 的灵活性和效率。

  • lru

记录对象最后一次被访问的时间,占24位;

  • refcount

记录的是该对象被引用的次数,类型为整型;refcount 的作用,主要在于对象的引用计数和内存回收。
Redis 为了节省内存,当有一些对象重复出现时,新的程序不会创建新的对象,而是仍然使用原来的对象。当对象的refcount>1时,称为共享对象。

  • ptr

指针指向底层数据结构的指针

常用类型和应用场景:

  • String:缓存、计数器、分布式锁等。
  • List:链表、队列、微博关注人时间轴列表等。
  • Hash:用户信息、Hash 表等。
  • Set:去重、赞、踩、共同好友等。
  • Zset:访问量排行榜、点击量排行榜等。

简单动态字符串SDS

Redis没有直接使用c语言传统的字符串,而是自己构建了一种名为简单动态字符串。简单动态字符串SDS是可变的,遵循C字符串以1字节空字符结尾,最大长度为512M。

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

优势:

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

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

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

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

字典dict

又称符号表,关联数组或映射,是一种用于保存键值对的抽象数据结构。

整个Redis库是用字典来存储的(K-V结构)。字典使用哈希表作为底层实现,一个字典带有两个哈希表,一个哈希表里面可以有多个哈希表节点,而每个哈希表节点就保存了字典中的一个键值对。

字典结构体

typedef struct dict {
    dictType *type; // 该字典对应的特定操作函数
    void *privdata; // 上述类型函数对应的可选参数
    dictht ht[2]; /* 两张哈希表,存储键值对数据,ht[0]为原生哈希表,ht[1]为 rehash 哈希表 */
    long rehashidx; /*rehash标识 当等于-1时表示没有在rehash,否则表示正在进行rehash操作,存储的值表示hash表 ht[0]的rehash进行到哪个索引值(数组下标)*/
    int iterators; // 当前运行的迭代器数量
} dict;
  • type字段,指向dictType结构体,每个dictType保存了一簇用于操作特定类型键值对的函数;
  • privdata属性保存了需要传给那些类型特定函数的可选参数;
  • ht属性是一个包含两个项的数组,数组中的每个项都是一个dictht哈希表,ht[1]只有在对ht[0]哈希表进行rehash操作时使用;
  • trehashidx属性是rehash索引,没有进行rehash操作时值都为-1。

哈希表结构体

typedef struct dictht {
    dictEntry **table; // 哈希表数组
    unsigned long size; // 哈希表数组的大小
    unsigned long sizemask; // 用于映射位置的掩码,值永远等于(size-1)
    unsigned long used; // 哈希表已有节点的数量,包含next单链表数据
} dictht;
  • table是一个数组,数组中的每个元素都是一个指向哈希表节点的指针,每个节点都保存着一个键值对;
  • size记录了哈希表的大小,也就是table数组的大小;
  • sizemask的值总是等于size-1,这个属性和哈希值一起决定一个键应该被放到table数组的那个索引上面;
  • used记录了哈希表目前已有节点的数量。

哈希表节点

typedef struct dictEntry {
    void *key; // 键
    union { // 值v的类型可以是以下4种类型
        void *val;
        uint64_t u64;
        int64_t s64;
        double d;
    } v;
    struct dictEntry *next; // 指向下一个哈希表节点,形成单向链表 解决hash冲突
} dictEntry;
  • key字段存储的是键值对中的键
  • v字段是个联合体,存储的是键值对中的值
  • next指向下一个哈希表节点,用于解决hash冲突

关系图

应用场景
1、主数据库的K-V数据存储
2、散列表对象(hash)
3、哨兵模式中的主从节点管理

跳跃表

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

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

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值