Redis的内部扩容机制

1,概述

Redis是一个键值对(key-value pair)数据库服务器,Redis服务器结构是redis.h/redisServer结构表示,Redis服务器中的所有数据库保存在db数组中,数据库的结构是redis.h/redisDb,其中,redisDb结构的dict字典保存了数据库中的所有键值对,所以,说起Redis的扩容机制,指的就是字典中哈希表的rehash(重新散列)操作。

struct redisServer{

//..

//服务器的数据库数量

int dbnum;

//一个数组,保存着服务器中的所有数据库

redisDb *db;

//..

}

struct redisDb{

//..

//数据库键空间,保存着数据库中的所有键值对

dict *dict;

//..

}

2,字典的扩容机制

2.1,普通状态下的字典:

typedef struct dict {

    // 类型特定函数

    dictType *type;

    // 私有数据

    void *privedata;

    // 哈希表

    dictht  ht[2];

    // rehash 索引

    in trehashidx;

}

typedef struct dictht {

  //哈希表数组

  dictEntry **table;

  //哈希表大小

  unsigned long size;

  //哈希表大小掩码,用于计算索引值

  unsigned long sizemask;

  //该哈希表已有节点的数量

  unsigned long used;

}

typeof struct dictEntry{

  //键

  void *key;

  //值

  union{

      void *val;

      uint64_tu64;

      int64_ts64;

  }

  struct dictEntry *next;

}

2.2,解决哈希冲突

当要将一个新的键值对添加到字典里面时,程序需要先根据键值对的键计算出哈希值和索引值,然后再根据索引值,将包含新键值对的哈希表节点放到哈希表数组的指定索引上。

当有两个或以上数量的键被分配到了哈希表数组的同一个索引上时,称为哈希冲突,Redis的哈希表使用单向链表解决冲突,使用哈希表节点的next指针将冲突的节点连接起来,因为dictEntry节点组成的链表没有指向链表表尾的指针,为了速度考虑,总是将新节点添加的链表的表头位置(复杂度为O(1))。

2.3,rehash

为了让哈希表的负载因子(load factor)维持在一个合理的范围内,会使用rehash(重新散列)操作对哈希表进行相应的扩展或收缩。

2.3.1,哈希表被扩展的条件:

1)Redis服务器目前没有在执行BGSAVE命令或BGREWRITEAOF命令,并且哈希表的负载因子大于等于1。

2)Redis服务器目前在执行BGSAVE命令或BGREWRITEAOF命令,并且哈希表的负载因子大于等于5。

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

当哈希表的负载因子小于0.1时,对哈希表执行收缩操作。

2.3.2,rehash的操作步骤

1)为字典的ht[1]哈希表分配空间。

如果执行的是扩展操作,那么ht[1] 的大小为第一个大于等于ht[0] .used*2的2的n次幂

如果执行的是收缩操作,那么ht[1] 的大小为第一个大于等于ht[0].used 的2的n次幂

因此这里我们为ht[1] 分配 空间为8

2)将ht[0]中的数据转移到ht[1]中,在转移的过程中,重新计算键的哈希值和索引值,然后将键值对放置到ht[1]的指定位置。

数据转移后的结果:

3)当ht[0]的所有键值对都迁移到了ht[1]之后(ht[0]变为空表),将ht[0]释放,然后将ht[1]设置成ht[0],最后为ht[1]分配一个空白哈希表:

2.3.3,渐近式rehash

上面我们说到,在进行拓展或者压缩的时候,可以直接将所有的键值对rehash 到ht[1]中,这是因为数据量比较小。在实际开发过程中,这个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 结束

    采用渐进式rehash 的好处在于它采取分而治之的方式,避免了集中式rehash 带来的庞大计算量。

参考书籍【Redis设计与实现】

文章:http://www.cnblogs.com/jaycekon/p/6227442.html

如有错误,请批评指正。

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