Redis底层数据结构

一、简单动态字符串(SDS)

        参考资料:http://redisbook.com/preview/sds/different_between_sds_and_c_string.html

1.数据结构

类型

结构体

动态字符串

struct sdshdr {

// buf 中已占用空间的长度
int len;
// buf 中剩余可用空间的长度
int free;
// 数据空间
char buf[];

};


2.应用范围

Redis 的默认字符串,字符键值对中应

3.优点(VS C语言的字符串)

    • 常数复杂度获取字符串长度。

    • 杜绝缓冲区溢出。

    • 减少修改字符串长度时所需的内存重分配次数。

    • 二进制安全。

    • 兼容部分 C 字符串函数。


二、链表

1.数据结构

 

类型

结构图

 双端链表节点

/*
* 双端链表节点
*/
typedef struct listNode {

// 前置节点
struct listNode *prev;

// 后置节点
struct listNode *next;

// 节点的值(void*表示无类型的指针)
void *value;

} listNode;

 

双端链表

/*
* 双端链表结构
*/
typedef struct list {

// 表头节点
listNode *head;

// 表尾节点
listNode *tail;

// 节点值复制函数
void *(*dup)(void *ptr);

// 节点值释放函数
void (*free)(void *ptr);

// 节点值对比函数
int (*match)(void *ptr, void *key);

// 链表所包含的节点数量
unsigned long len;

} list;

       

2.应用范围

     列表


 

三、字典

1.数据结构

 

类型

结构体

哈希表

/*
* 哈希表
*
* 每个字典都使用两个哈希表,从而实现渐进式 rehash 。
*/
typedef struct dictht {

// 哈希表数组
dictEntry **table;

// 哈希表大小
unsigned long size;

// 哈希表大小掩码,用于计算索引值
// 总是等于 size - 1
unsigned long sizemask;

// 该哈希表已有节点的数量
unsigned long used;

} dictht;

哈希表节点

/*
* 哈希表节点
*/
typedef struct dictEntry {

// 键
void *key;

// 值
union {
void *val;
uint64_t u64;
int64_t s64;
} v;

// 指向下个哈希表节点,形成链表
struct dictEntry *next;

} dictEntry;

字典

/*
* 字典
*/
typedef struct dict {

// 类型特定函数
dictType *type;

// 私有数据
void *privdata;

// 哈希表
dictht ht[2];

// rehash 索引
// 当 rehash 不在进行时,值为 -1
int rehashidx; /* rehashing not in progress if rehashidx == -1 */

// 目前正在运行的安全迭代器的数量
int iterators; /* number of iterators currently running */

} dict;

2. Rehash过程

      负载因子

 

               负载因子 = 哈希表已保存节点数量 / 哈希表大小(load_factor = ht[0].used / ht[0].size)

      哈希表空间分配规则:
      如果执行的是拓展操作,那么ht[1] 的大小为第一个大于等于ht[0] 的2的n次幂
      如果执行的是收缩操作,那么ht[1] 的大小为第一个大于等于ht[0] 的2的n次幂

     过程:

              分配空间->数据转移->释放ht[0]

3.渐进式 rehash

       渐进式rehash 的详细步骤:
      1、为ht[1] 分配空间,让字典同时持有ht[0]和ht[1]两个哈希表
      2、在几点钟维持一个索引计数器变量rehashidx,并将它的值设置为0,表示rehash 开始
      3、在rehash 进行期间,每次对字典执行CRUD操作时,程序除了执行指定的操作以外,还会将ht[0]中的数据rehash 到ht[1]表中,并且将rehashidx加一
      4、当ht[0]中所有数据转移到ht[1]中时,将rehashidx 设置成-1,表示rehash 结束

 

4.应用

     Map


 

四、跳跃表

        跳跃表原理:https://www.cnblogs.com/thrillerz/p/4505550.html

        参考资料:http://redisbook.com/preview/skiplist/datastruct.html

1.数据结构

类型

结构体

跳跃表节点

/*
* 跳跃表节点
*/
typedef struct zskiplistNode {

// 成员对象
robj *obj;

// 分值
double score;

// 后退指针
struct zskiplistNode *backward;

// 层
struct zskiplistLevel {

// 前进指针
struct zskiplistNode *forward;

// 跨度
unsigned int span;

} level[];

} zskiplistNode;

跳跃表

/*
* 跳跃表
*/
typedef struct zskiplist {

// 表头节点和表尾节点
struct zskiplistNode *header, *tail;

// 表中节点的数量
unsigned long length;

// 表中层数最大的节点的层数
int level;

} zskiplist;

2.层高的确定

       每个跳跃表节点的层高都是 1 至 32 之间的随机数

3.应用

      有序集合


 

五、整数集合

1.数据结构

类型

结构体

整数集合

typedef struct intset {

// 编码方式
uint32_t encoding;

// 集合包含的元素数量
uint32_t length;

// 保存元素的数组
int8_t contents[];

} intset;

 

2.升级

    升级整数集合并添加新元素共分为三步进行:

        1) 根据新元素的类型,扩展整数集合底层数组的空间大小,并为新元素分配空间

        2) 将底层数组现有的所有元素都转换成新的编码格式,重新分配空间

     3) 将新元素加入到底层数组中

3.应用

  集合


 

六、压缩队列

1.数据结构

类型结构体说明
压缩列表

 

1、zlbytes(4个字节):用于记录整个压缩列表占用的内存字节数

2、zltail(4个字节):记录要列表尾节点距离压缩列表的起始地址有多少字节

3、zllen(2个字节):记录了压缩列表包含的节点数量。

4、entryX:要说列表包含的各个节点

5、zlend(1个字节):用于标记压缩列表的末端

ziplist 节点

/*
* 保存 ziplist 节点信息的结构
*/
typedef struct zlentry {

// prevrawlen :前置节点的长度
// prevrawlensize :编码 prevrawlen 所需的字节大小
unsigned int prevrawlensize, prevrawlen;

// len :当前节点值的长度
// lensize :编码 len 所需的字节大小
unsigned int lensize, len;

// 当前节点 header 的大小
// 等于 prevrawlensize + lensize
unsigned int headersize;

// 当前节点值所使用的编码类型
unsigned char encoding;

// 指向当前节点的指针
unsigned char *p;

} zlentry;

 

2.应用

         列表键和哈希键


 

七、Redis 对象

 1.Redis 对象的结构

        Redis 并没有直接使用这些数据结构来实现键值对数据库, 而是基于这些数据结构创建了一个对象系统,对象的结构如下:

对象结构体

对象类型

编码方式

typedef struct redisObject {

// 类型

//判断一个对象是否可以执行给定的命令
unsigned type:4;
// 编码

//不同的使用场景, 为对象设置多种不同的数据结构实现
unsigned encoding:4;
// 对象最后一次被访问的时间
unsigned lru:REDIS_LRU_BITS; /* lru time (relative to server.lruclock) */
// 引用计数
int refcount;
// 指向实际值的指针
void *ptr;

} robj;

/* Object types */
// 对象类型
#define REDIS_STRING 0
#define REDIS_LIST 1
#define REDIS_SET 2
#define REDIS_ZSET 3
#define REDIS_HASH 4
// 对象编码
#define REDIS_ENCODING_RAW 0 /* Raw representation */
#define REDIS_ENCODING_INT 1 /* Encoded as integer */
#define REDIS_ENCODING_HT 2 /* Encoded as hash table */
#define REDIS_ENCODING_ZIPMAP 3 /* Encoded as zipmap */
#define REDIS_ENCODING_LINKEDLIST 4 /* Encoded as regular linked list */
#define REDIS_ENCODING_ZIPLIST 5 /* Encoded as ziplist */
#define REDIS_ENCODING_INTSET 6 /* Encoded as intset */
#define REDIS_ENCODING_SKIPLIST 7 /* Encoded as skiplist */
#define REDIS_ENCODING_EMBSTR 8 /* Embedded sds string encoding */

 

2.类型和编码及对象直接的对应关系

        通过 encoding 属性来设定对象所使用的编码, 而不是为特定类型的对象关联一种固定的编码, 极大地提升了 Redis 的灵活性和效率。

类型

编码

对象

REDIS_STRINGREDIS_ENCODING_INT使用整数值实现的字符串对象。
REDIS_STRINGREDIS_ENCODING_EMBSTR使用 embstr 编码的简单动态字符串实现的字符串对象。
REDIS_STRINGREDIS_ENCODING_RAW使用简单动态字符串实现的字符串对象。
REDIS_LISTREDIS_ENCODING_ZIPLIST使用压缩列表实现的列表对象。
REDIS_LISTREDIS_ENCODING_LINKEDLIST使用双端链表实现的列表对象。
REDIS_HASHREDIS_ENCODING_ZIPLIST使用压缩列表实现的哈希对象。
REDIS_HASHREDIS_ENCODING_HT使用字典实现的哈希对象。
REDIS_SETREDIS_ENCODING_INTSET使用整数集合实现的集合对象。
REDIS_SETREDIS_ENCODING_HT使用字典实现的集合对象。
REDIS_ZSETREDIS_ENCODING_ZIPLIST使用压缩列表实现的有序集合对象。
REDIS_ZSETREDIS_ENCODING_SKIPLIST使用跳跃表和字典实现的有序集合对象。

 

3. Redis5种对象的结构(object.c)

对象类型编码结构场景
字符串对象 int 

字符串对象可以保存为整数值, 并且这个整数值可以用 long 类型来表示
raw 

保存的是一个字符串值, 并且这个字符串值的长度大于39 字节
embstr

保存的是一个字符串值, 并且这个字符串值的长度小于等于 39 字节
列表对象ziplist

  1. 列表对象保存的所有字符串元素的长度都小于 64 字节;
  2. 列表对象保存的元素数量小于 512 个;
linkedlist

不满足上面条件
哈希对象 ziplist

  1. 哈希对象保存的所有键值对的键和值的字符串长度都小于 64 字节;
  2. 哈希对象保存的键值对数量小于 512 个;
hashtable 

不满足上面条件
集合对象intset 

  1. 集合对象保存的所有元素都是整数值;
  2. 集合对象保存的元素数量不超过 512 个;
hashtable

不满足上面条件
有序集合对象ziplist

  1. 有序集合保存的元素数量小于 128 个;
  2. 有序集合保存的所有元素成员的长度都小于 64 字节;
skiplist

不满足上面条件

 


 

八、Redis数据库的结构

               redis数据库的k-v结构如下所示:

typedef struct redisDb {

// 数据库键空间,保存着数据库中的所有键值对
dict *dict; /* The keyspace for this DB */

// 键的过期时间,字典的键为键,字典的值为过期事件 UNIX 时间戳
dict *expires; /* Timeout of keys with a timeout set */

// 正处于阻塞状态的键
dict *blocking_keys; /* Keys with clients waiting for data (BLPOP) */

// 可以解除阻塞的键
dict *ready_keys; /* Blocked keys that received a PUSH */

// 正在被 WATCH 命令监视的键
dict *watched_keys; /* WATCHED keys for MULTI/EXEC CAS */

struct evictionPoolEntry *eviction_pool; /* Eviction pool of keys */

// 数据库号码
int id; /* Database ID */

// 数据库的键的平均 TTL ,统计信息
long long avg_ttl; /* Average TTL, just for stats */

} redisDb;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值