第 4 章 字典

dictEntry,node of a map

typedef struct dictEntry{
  void *key;
  union{
    void *val;
    uint64_t u64;
    int64_t s64;
  }v;
  struct dictEntry *next;
} dictEntry;

其中键值对的值可以是一个指针,或者是一个 uint64_t 整数,又或者是一个 int64_t 整数。
next 属性是指向另一个哈希节点的指针,这个指针可以将多个哈希值相同的键值对连接在一起,以此来解决键冲突的问题。也就是说,Redis 的哈希表使用了链地址法来解决键冲突。

dictht(dict hash table)

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

map

 typedef struct dict{
  dictType *type;
  void *privdata;
  dictht ht[2];
  int trehashidx;
} dict;

ht 属性是一个包含两个项的数组,数组中的每个项都是一个 dictht 哈希表,一般情况下,字典只使用 ht[0] 哈希表,ht[1] 哈希表只会在对 ht[0] 哈希进行 rehash 时使用。
除了 ht[1] 之外,另一个与 rehash 有关的属性就是 rehashidx,它记录了 rehash 目前的进度,如果目前没有在进行 rehash,那么它的值为 -1.

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;

每个 dictType 结构保存了一簇用于操作特定类型键值对的函数,Redis 会为用途不同的字典设置不同的类型特定函数。

hashFunction

Redis 计算哈希值和索引值的方法如下:

int hash = dict->type->hashFunction(key);

int index = hash & dict->ht[x].sizemask;

Redis 使用 MurmurHash2 算法来计算键的哈希值。

rehash

为了让哈希表的负载因子维持在一个合理的范围之内,当哈希表保存的键值对数量太多或者太少时,程序需要对哈希表的大小进行相应的扩展或者收缩。
Redis 对字典的哈希表执行 rehash 的步骤如下:

计算哈希表的负载因子公式:
load_factor = ht[0].used / ht[0].size

  1. 为字典的 ht[1] 哈希表分配空间,这个哈希表的空间大小取决于要执行的操作,以及 ht[0] 当前包含的键值对数量(ht[0].usedsize):
  • 如果执行的是扩展操作,那么 ht[1] 的大小为第一个大于等于 ht[0].used*2 的 2^n.
  • 如果执行的是收缩操作,那么 ht[1] 的大小为第一个大于等于 ht[0].used 的 2^n.
int getRehashSize(){
  int tag = 0;
  if(扩展){
    tag = ht[1].used * 2;
  }else{
    tag = ht[1].used;
  }
  int size = 1;
  while(size < tag){
    size *= 2;
  }
  return size;
}
  1. 将保存在 ht[0] 中的所有键值对 rehash 到 ht[1] 上面。
  2. ht[0] 包含的所有键值对都迁移到了 ht[1] 之后,释放 ht[0],将 ht[1] 设置为 ht[0],并在 ht[1] 新创建一个空白哈希表,为下一次 rehash 做准备。

When will rehash happen?

扩容

  1. 服务器目前没有执行 BGSAVE 命令或者 BGREWRITEAOF 命令,并且哈希表的负载因子大于等于 1.
  2. 服务器目前正在执行 BGSAVE 命令或者 BGREWRITEAOF 命令,并且哈系表的负载因子大于等于 5.

收缩

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

渐进式 rehash

  1. 每次对字典执行添加、删除、查找或者更新操作时,程序除了执行指定的操作意外,还会顺带将 ht[0] 哈希表在 rehashidx 索引上的所有键值对 rehash 到 ht[1]
  2. rehashidx 为 0,表示开始 rehash;每完成一次对 rehashidx 的 rehash 操作,rehashidx 增加1; rehash 完成,rehashidx 为 -1.

渐进式 rehash 执行期间的哈希表操作

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

推荐阅读更多精彩内容

  • 字典,java中的map,是一种用于保存键值对(key-value pair)的抽象数据结构。字典中,一个键(ke...
    猪大金阅读 370评论 0 0
  • 字典,又称为符号表,关联数组,或映射,是一种用于保存键值对的抽象数据结构。字典中每个键都是独一无二的,可以根据键查...
    亮亮_ff3d阅读 197评论 0 0
  • 1、Redis字典介绍 ​ Redis字典使用散列表最为底层实现,一个散列表里面有多个散列表节点,每个散列表...
    先弓阅读 150评论 0 0
  • 字典在Redis中的应用相当广泛,比如Redis的数据库就是使用字典来作为底层实现的,对数据库的增、删、查、改操作...
    Felicia1993阅读 853评论 0 0
  • 字典 1. 字典的实现 Redis的字典使用哈希表作为底层实现,一个哈希表里面可以有多个哈希表节点,每个哈希表节点...
    xMustang阅读 138评论 0 0