Redis基础数据结构学习,笔记1.

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/recall_yesterday/article/details/82832379

redis 有5种数据类型。

  • 1.字符串 String
  • 2.列表 list
  • 3.集合 set
  • 4.有序集合 zset
  • 5.哈希字典 hash
    那么他们是如何实现的呢?那就要先对其底层的数据结构进行下手了。

一 动态字符串

字符串的底层是一个名叫做简单动态字符串的东西,其实是相当于对C中对一个字符串数组做了封装。
struct sds {
char * data;
unsigned len;
unsigned free;
};
这种设计对好处是,

  • 可以O(1)获取字符串长度。
  • 可以使用C中的字符串处理函数,并且由于有了free字段,可以进行越界检查,避免写越界。
  • 可以减少内存的分配,进行重复利用。这一点体现在预先分配较大内存,和len实现的。
  • 二进制安全,因为即使存在着空字符比如\0也无妨,因为它是按照len字段来获取内容的。而不是C中的函数,按照\0来结尾。

二 链表

在C++中有stl的链表可以使用,但是C中是没有的。所以redis自己实现了链表,具体的说是双向链表。

简单的示例:
struct ListNode{
void * pdata;
struct ListNode * preNode;
struct ListNode * nextNode;
};

struct List{
struct ListNode * head;
struct ListNode * tail;
unsigned len;
    // 节点值复制函数
    void *(*dup)(void *ptr);

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

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

上面就是一个链表结构,这样的实现,有一些好处:

  • 双端链表,获取前后节点的复杂度O(1),长度也是O(1)
  • 多态,可以兼容任何类型。实现不同的函数就行。

三 哈希字典

redis的字典是使用hash表来实现的。采用链表的方式解决hash冲突。MurmurHash2算法计算hash值。相对上面的数据结构这个稍微有点复杂。
hash的节点结构如下:

struct hashNode{
  struct hashNode * next;
  void * key;
  union data{
  void * data;
  uint64 u64;
  int64 s64;
} v;
};

上面是一个hash的节点,下面是一个hash表的结构
struct hashList
{
  hashNode **table;
  int used;//已经使用的节点
  int size;//哈希表的大小
  int marksize;//用来计算hash值的。通过位与的结果选择hash节点。
};

那么字典的结构就呼之欲出了。
struct dict{
struct hashList ht[2];//使用两个hash表,来避免rehash时的问题。
struct dictType * dicttype;//数据类型,这个是使字典支持多态的原因
int rehashindex;//当不在进行rehash的时候为-1
void * privateData;//私有数据
};
typedef struct dictType {

   // 计算哈希值的函数
   unsigned int (*hashFunction)(const void *key);

   // 复制键的函数
   void *(*keyDup)(void *privdata, const void *key);

   // 复制值的函数
   void *(*valDup)(void *privdata, const void *obj);

   // 对比键的函数
   int (*keyCompare)(void *privdata, const void *key1, const void *key2);

   // 销毁键的函数
   void (*keyDestructor)(void *privdata, void *key);

   // 销毁值的函数
   void (*valDestructor)(void *privdata, void *obj);

} dictType;

四 跳跃表

跳跃表是一种能和平衡树相媲美的数据结构,但是相对于树,来说,实现起来更加简单一点,涉及的都是链表相关的操作。

五 整数集合

typedef struct intset {

    // 编码方式
    uint32_t encoding;

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

    // 保存元素的数组,这个是一个有序的不重复的整数集合。
    int8_t contents[];

} intset;
这个整数集合,是可以进行升级的,而不只是存储int8的整数,因为有encoding字段,所以可以存储不同类型的整数。

整数集合的升级策略。
1.当新添加的数据的数据类型的长度比现有集合的长度要大的话,才进行升级。
2.升级时,先按照新数据类型的长度重新分配内存,并将所有现存的数据转换成新数据类型大小。
3.然后将新数据插入到数组中,保持数据有序。

六 压缩列表

redis 为了节省内存还有一种数据结构叫做压缩列表,类似结构如下:

struct zipList{
uint32_t  bytes;//总字节数,内存大小
uint32_t tail;//末尾元素的到起始位置的偏移量。
uint32_t size;//元素的个数
void * data;//元素中的数据
int8_t zend;//用来标示,压缩列表的结尾。是一个特殊值。
};

元素的结构:
struct {
previous_entry_length ;//记录了上一个节点的长度
encoding;//记录当前节点内容的格式。
content;//存储内容。
};

redis 的对象,个人感觉是一种通用的类型。是在上面的数据结构的更抽象的一层。

结构类似:

typedef struct redisObject {

    // 类型
    unsigned type:4;

    // 编码
    unsigned encoding:4;

    // 指向底层实现数据结构的指针
    void *ptr;

    // ...

} robj;


类型常量	对象的名称
REDIS_STRING	字符串对象
REDIS_LIST	列表对象
REDIS_HASH	哈希对象
REDIS_SET	集合对象
REDIS_ZSET	有序集合对象


编码常量	编码所对应的底层数据结构
REDIS_ENCODING_INT	long 类型的整数
REDIS_ENCODING_EMBSTR	embstr 编码的简单动态字符串
REDIS_ENCODING_RAW	简单动态字符串
REDIS_ENCODING_HT	字典
REDIS_ENCODING_LINKEDLIST	双端链表
REDIS_ENCODING_ZIPLIST	压缩列表
REDIS_ENCODING_INTSET	整数集合
REDIS_ENCODING_SKIPLIST	跳跃表和字典

可以说是,每个对象都至少两个数据类型可以实现
类型 编码 对象
REDIS_STRING REDIS_ENCODING_INT 使用整数值实现的字符串对象。
REDIS_STRING REDIS_ENCODING_EMBSTR 使用 embstr 编码的简单动态字符串实现的字符串对象。
REDIS_STRING REDIS_ENCODING_RAW 使用简单动态字符串实现的字符串对象。
REDIS_LIST REDIS_ENCODING_ZIPLIST 使用压缩列表实现的列表对象。
REDIS_LIST REDIS_ENCODING_LINKEDLIST 使用双端链表实现的列表对象。
REDIS_HASH REDIS_ENCODING_ZIPLIST 使用压缩列表实现的哈希对象。
REDIS_HASH REDIS_ENCODING_HT 使用字典实现的哈希对象。
REDIS_SET REDIS_ENCODING_INTSET 使用整数集合实现的集合对象。
REDIS_SET REDIS_ENCODING_HT 使用字典实现的集合对象。
REDIS_ZSET REDIS_ENCODING_ZIPLIST 使用压缩列表实现的有序集合对象。
REDIS_ZSET REDIS_ENCODING_SKIPLIST 使用跳跃表和字典实现的有序集合对象。

参考,redis设计与实现

猜你喜欢

转载自blog.csdn.net/recall_yesterday/article/details/82832379