Redis基本数据结构和原理

简介:

Redis是一个key-value存储系统,在各种系统中的使用率越来越高,大部分原因是因为其高性能的特性。Redis拥有非常多的优势,读写性能高--100000次/s以上的读速度,80000次/s以上的写速度;Redis的所有操作都是单线程原子性的,支持丰富的特性,如支持订阅-发布模式,通知,设置key过期特性。

String(SDS)

上边设置key=msg,value=hello world的键值对,它们的底层存储是:键(key)是字符串类型,其底层实现是一个保存着“msg”的SDS。值(value)是字符串类型,其底层实现是一个保存着“hello world”的SDS。

SDS的结构

/*  
 * 保存字符串对象的结构  
 */  
struct sdshdr {  
      
    // buf 中已占用空间的长度  
    int len;  
  
    // buf 中剩余可用空间的长度  
    int free;  
  
    // 数据空间  
    char buf[];  
};

使用SDS的好处:
1.防止在改变某个字符串时缓冲区溢出。
2.减少扩展或收缩字符串带来的内存重分配次数
空间预分配,惰性空间分配

链表(list的实现方式之一)

链表是list的实现方式之一。当list包含了数量较多的元素,或者列表中包含的元素都是比较长的字符串时,Redis会使用链表作为实现List的底层实现。此链表是双向链表:

typedef struct listNode{
      struct listNode *prev;
      struct listNode * next;
      void * value;  
}

list结构

typedef struct list{
    //表头节点
    listNode  * head;
    //表尾节点
    listNode  * tail;
    //链表长度
    unsigned long len;
    //节点值复制函数
    void *(*dup) (void *ptr);
    //节点值释放函数
    void (*free) (void *ptr);
    //节点值对比函数
    int (*match)(void *ptr, void *key);
}

下面是list的数据结构图


image.png

字典

在字典中,一个键(key)可以和一个值(value)进行关联,字典中的每个键都是独一无二的。
Redis本身的K-V存储就是利用字典这种数据结构的,value类型的哈希表也是通过这个实现的。
下面是哈希表的定义:

typedef struct dictht {
   //哈希表数组
   dictEntry **table;
   //哈希表大小
   unsigned long size;
 
   //哈希表大小掩码,用于计算索引值
   unsigned long sizemask;
   //该哈希表已有节点的数量
   unsigned long used;
}

下面是dictEntry的数据结构

typeof struct dictEntry{
   //键
   void *key;
   //值
   union{
      void *val;
      uint64_tu64;
      int64_ts64;
   }
   struct dictEntry *next;
}

我们会发现这样会存在hash冲突,解决的办法是采用链地址法来解决hash冲突。
Redis在dictht的基础上,又抽象了一层字典dict.

typedef struct dict {
    // 类型特定函数
    dictType *type;
    // 私有数据
    void *privedata;
    // 哈希表
    dictht  ht[2];
    // rehash 索引
    in trehashidx;
}

扩充Rehash:随着对哈希表的不断操作,哈希表保存的键值对会逐渐的发生改变,为了让哈希表的负载因子维持在一个合理的范围之内,我们需要对哈希表的大小进行相应的扩展或者压缩,这时候,我们可以通过 rehash(重新散列)操作来完成。其实现方式和hashmap略有不同,因为dict有两个hash表dictht,所以它是通过这两个dictht互相进行转移的。

渐进式Rehash:在实际开发过程中,这个rehash 操作并不是一次性、集中式完成的,而是分多次、渐进式地完成的。采用渐进式rehash 的好处在于它采取分而治之的方式,避免了集中式rehash 带来的庞大计算量。详细步骤如下:
1.为ht[1] 分配空间,让字典同时持有ht[0]和ht[1]两个哈希表
2.维持一个索引计数器变量rehashidx,并将它的值设置为0,表示rehash的开始。
3.在rehash进行期间,每次对字典执行CURD操作时,程序除了执行指定的操作以外,还会将ht[0]中的数据rehash到ht[1]表中,并且将rehashidx加一
4.当ht[0]中所有数据转移到ht[1]中时,将rehashidx设置成-1,表示rehash结束。

跳跃表

Redis 只在两个地方用到了跳跃表,一个是实现有序集合键(sorted Sets),另外一个是在集群节点中用作内部数据结构。
其实跳表主要是来替代平衡二叉树的,比起平衡树来说,跳表的实现要简单直观的多。跳跃表(skiplist)是一种有序数据结构,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速查找访问节点的目的。跳跃表是一种随机化的数据,跳跃表以有序的方式在层次化的链表中保存元素,效率和平衡树媲美 ——查找、删除、添加等操作都可以在O(logn)期望时间下完成。
Redis 的跳跃表 主要由两部分组成:zskiplist(链表)和zskiplistNode (节点)

typedef struct zskiplistNode{  
   //层  
     struct zskiplistLevel{  
     //前进指针  
        struct zskiplistNode *forward;  
    //跨度  
        unsigned int span;  
    } level[];  
  //后退指针  
    struct zskiplistNode *backward;  
  //分值  
    double score;  
  //成员对象  
    robj *obj;  
}

typedef struct zskiplist {  
     //表头节点和表尾节点  
     structz skiplistNode *header,*tail;  
     //表中节点数量  
     unsigned long length;  
     //表中层数最大的节点的层数  
     int level;  
  
}zskiplist;

1.层:level 数组可以包含多个元素,每个元素都包含一个指向其他节点的指针。level数组的每个元素都包含:前进指针:用于指向表尾方向的前进指针,跨度:用于记录两个节点之间的距离.
2.后退指针:用于从表尾向表头方向访问节点
3.分值和成员:跳跃表中的所有节点都按分值从小到大排序(按照这个进行排序的,也就是平衡二叉树(搜索树的)的节点大小)。成员对象指向一个字符串,这个字符串对象保存着一个SDS值(实际存储的值)

此文是对《redis设计与实现》总结

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,640评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,254评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,011评论 0 355
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,755评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,774评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,610评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,352评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,257评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,717评论 1 315
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,894评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,021评论 1 350
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,735评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,354评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,936评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,054评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,224评论 3 371
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,974评论 2 355

推荐阅读更多精彩内容

  • 前言 Redis是目前最火爆的内存数据库之一,通过在内存中读写数据,大大提高了读写速度,可以说Redis是实现网站...
    Java架构阅读 1,278评论 1 16
  • 曾经以为我这个理工女博士和艺术无缘,也没有什么艺术天赋。艺术在我的眼里是有天赋的人加上苦练的技艺才能达成的。 儿子...
    守望智慧阅读 742评论 3 5