声明:本篇博客内容来自《Redis深度历险》一书,略有改动
字典的结构
在 Redis 中所有的 key 都存储在一个很大的字典中,这个字典的结构和 Java 中的 HashMap 一样,是一维数组 + 二维链表结构,如下图,第一维数组的大小总是 2^n(n>=0),扩容一 次数组大小空间加倍,也就是 n++
scan 指令返回的游标就是第一维数组的位置索引,我们将这个位置索引称为槽 (slot)。 如果不考虑字典的扩容缩容,直接按数组下标挨个遍历就行了。limit 参数就表示需要遍历的槽位数,之所以返回的结果可能多可能少,是因为不是所有的槽位上都会挂接链表,有些槽位可能是空的,还有些槽位上挂接的链表上的元素可能会有多个。每一次遍历都会将 limit 数量的槽位上挂接的所有链表元素进行模式匹配过滤后,一次性返回给客户端
scan 历顺序
scan 的遍历顺序非常特别。它不是从第一维数组的第 0 位一直遍历到末尾,而是采用了高位进位加法来遍历。之所以使用这样特殊的方式进行遍历,是考虑到字典的扩容和缩容时避免槽位的遍历重复和遗漏
普通加法和高位进位加法的区别
高位进位法从左边加,进位往右边移动,同普通加法正好相反。但是最终它们都会遍历所有的槽位并且没有重复
字典扩容
Java 中的 HashMap 有扩容的概念,当 loadFactor 达到阈值时,需要重新分配一个新的2 倍大小的数组,然后将所有的元素全部 rehash 挂到新的数组下面。rehash 就是将元素的hash 值对数组长度进行取模运算,因为长度变了,所以每个元素挂接的槽位可能也发生了变 化。又因为数组的长度是 2^n 次方,所以取模运算等价于位与操作
a mod 8 = a & (8-1) = a & 7
a mod 16 = a & (16-1) = a & 15
a mod 32 = a & (32-1) = a & 31
这里的 7, 15, 31 称之为字典的 mask 值,mask 的作用就是保留 hash 值的低位,高位都被设置为0,接下来我们看看 rehash 前后元素槽位的变化
假设当前的字典的数组长度由 8 位扩容到 16 位,那么 3 号槽位 011 将会被 rehash 到 3 号槽位和 11 号槽位,也就是说该槽位链表中大约有一半的元素还是 3 号槽位,其它的元素会放到 11 号槽位,11 这个数字的二进制是 1011,就是对 3 的二进制 011 增加了
一个高位 1,如下图
抽象一点说,假设开始槽位的二进制数是 xxx,那么该槽位中的元素将被 rehash 到 0xxx 和 1xxx(xxx+8) 中。 如果字典长度由 16 位扩容到 32 位,那么对于二进制槽位 xxxx 中的元素将被 rehash 到 0xxxx 和 1xxxx(xxxx+16) 中
对比扩容缩容前后的遍历顺序 ,如下图
观察这张图,我们发现采用高位进位加法的遍历顺序,无论是扩容还是缩容,rehash 后的槽位在遍历顺序上是相邻的
假设当前要即将遍历 110 这个位置 (橙色)
扩容后,当前槽位上所有的元素对应的新槽位是 0110 和 1110(深绿色),也就是在槽位的二进制数增加一个高位 0 或 1。这时我们可以直接从 0110 这个槽位开始往后继续遍历,而0110 槽位之前的所有槽位都是已经遍历过的,这样就可以避免扩容后对已经遍历过的槽位进行重复遍历
假设当前要即将遍历 110 这个位置 (橙色)
缩容后,当前槽位所有的元素对应的新槽位是 10(深绿色),也就是去掉槽位二进制最高位。这时我们可以直接从 10 这个槽位继续往后遍历,10 槽位之前的所有槽位都是已经遍历过的,这样就可以避免缩容的重复遍历。不过缩容还是不太一样,它会对图中 010 这个槽位上的元素进行重复遍历,因为缩融后 10 槽位的元素是 010 和 110 上挂接的元素的融合,注意:这也是为什么scan命令可能会返回重复key的根本原因!
渐进式 rehash
Java 的 HashMap 在扩容时会一次性将旧数组下挂接的元素全部转移到新数组下面。如果 HashMap 中元素特别多,线程就会出现卡顿现象
Redis 为了解决这个问题,它采用渐进式 rehash。它会同时保留旧数组和新数组,然后在定时任务中以及后续对 hash 的指令操作中渐渐地将旧数组中挂接的元素迁移到新数组上。这意味着要操作处于 rehash 中的字典,需要同时访问新旧两个数组结构。如果在旧数组下面找不到元素,还需要去新数组下面去寻找。scan也需要考虑这个问题,对与 rehash 中的字典,它需要同时扫描新旧槽位,然后将结果融合后返回给客户端。

redis之rehash原理相关推荐

  1. Redis源码-Set:Redis Set存储原理、Redis Set集合操作命令、Redis Set两种存储底层编码intset+hashtable、Redis Set应用场景

    Redis源码-Set:Redis Set存储原理.Redis Set集合操作命令.Redis Set两种存储底层编码intset+hashtable.Redis Set应用场景 Redis数据类型 ...

  2. Redis源码-ZSet:Redis ZSet存储原理、Redis ZSet命令、 Redis ZSet两种存储底层编码ziplist/dict+skiplist、Redis ZSet应用场景

    Redis源码-ZSet:Redis ZSet存储原理.Redis ZSet命令. Redis ZSet两种存储底层编码ziplist/dict+skiplist.Redis ZSet应用场景 Red ...

  3. redis高并发原理_Java中的42行代码中的URL缩短服务— Java(?!)Spring Boot + Redis...

    redis高并发原理 显然,编写URL缩短服务是新的"世界,您好! "在物联网/微服务/时代的世界中. 一切始于在45行Scala中的URL缩短服务-整洁的Scala,以Spray ...

  4. Redis scan命令原理

    scan类型命令 SCAN cursor [MATCH pattern] [COUNT count]SSCAN KEY cursor [MATCH pattern] [COUNT count]HSCA ...

  5. Redis 主从复制的原理及演进

    本文作者:百度基础架构部工程师,王钰 Redis 的主从复制经历了多次演进,本文将从最基本的原理和实现讲起,并层层递进,逐步呈现 Redis 主从复制的演进历史.大家将了解到 Redis 主从复制的原 ...

  6. Redis压缩列表原理与应用分析

    摘要 Redis是一款著名的key-value内存数据库软件,同时也是一款卓越的数据结构服务软件.它支持字符串.列表.哈希表.集合.有序集合五种数据结构类型,同时每种数据结构类型针对不同的应用场景又支 ...

  7. Redis 压缩列表原理与应用分析

    作者 | 西瓜 来源 | JAVA架构进阶之路 摘要 Redis是一款著名的key-value内存数据库软件,同时也是一款卓越的数据结构服务软件.它支持字符串.列表.哈希表.集合.有序集合五种数据结构 ...

  8. Redis 发布订阅原理以及springboo中RedisTemplate集成

    一.Redis发布订阅原理 Redis的架构包括两个部分:Redis Client和Redis Server,即客户端和服务端.客户端负责向服务器端发送请求并接受来自服务器端的响应.服务器端负责处理客 ...

  9. Redis源码-String:Redis String命令、Redis String存储原理、Redis String三种编码类型、Redis字符串SDS源码解析、Redis String应用场景

    Redis源码-String:Redis String命令.Redis String存储原理.Redis String三种编码类型.Redis字符串SDS源码解析.Redis String应用场景 R ...

最新文章

  1. 优先发展智慧旅游与智慧交通领域
  2. string之substring的用法
  3. Python:数组添加数据和删除数据
  4. 围成一圈的排列组合问题_分班必考知识点!小学奥数之排列组合问题
  5. [NodeJs] 如何使用nodejs对base64进行编解码?
  6. jquery 判断点击次数_jquery编程开发实现点击页面计算点击次数
  7. Spring mvc @RequestParam
  8. 苹果新一代 AirPods 能活过两年吗?
  9. 软件的一些标号及对应版本
  10. JavaScript —— Symbol数据类型的拓展
  11. 偶数支足球队进行单循环比赛,按照指定算法打印每轮的对阵形势
  12. RDKit | RDKit分子结构图的详细说明
  13. 基于单片机的电子密码锁设计
  14. 巨头林立的音频赛道,喜马拉雅如何讲好资本故事?
  15. Addressable 增量包
  16. 关于for丶foreach丶iterator 迭代器
  17. allure如何设置新logo
  18. Win10重装win7时一直显示windows启动中,不要慌
  19. Webpack 究竟是什么?如何理解Webpack
  20. Autofac之类型注册

热门文章

  1. ChainMapper和ChainReducer
  2. 超简单JS实现把鼠标选中文字发送到新浪微博
  3. 组图:2000悉尼奥运会
  4. raw_input 与 input的区别
  5. 中石油训练赛 - High Load Database(二分+记忆化)
  6. pandas 季度_pandas_时间序列和常用操作
  7. 如何产生高斯带限白噪声数据_车间噪声对我们的身体产生巨大影响,我们该如何解决?...
  8. c语言hash存储,C语言实现HashTable(一) 介绍
  9. 木马开机启动的六种方法
  10. 内存管理:_CrtDumpMemoryLeaks和_CrtSetBreakAlloc