当前位置: 首页 > news >正文

Redis渐进式rehash小疑问

一、rehash是什么

        在Redis中,Rehash是指在进行哈希表扩容或缩容时重新计算和重新分配哈希槽的过程。Redis使用哈希表来存储键值对,哈希表中的每个槽位对应一个哈希槽,每个槽位可以存储多个键值对。 当哈希表的负载因子(load factor)超过一定阈值时,Redis会触发Rehash操作来扩容哈希表,以提供更多的槽位来存储新的键值对。Rehash的过程如下:

  1. 创建新哈希表:在扩容之前,Redis会创建一个新的空哈希表,大小是原始哈希表的两倍。新哈希表中的槽位只有一小部分被使用,其他槽位为空。
  2. 迁移数据:Redis会从原始哈希表中逐个遍历槽位,将其中的数据逐步迁移到新哈希表中。每次迁移一个槽位的数据。
  3. 增量迁移:在迁移数据的过程中,Redis会将新写入的数据直接存储在新哈希表中。这样可以避免在迁移期间对原始哈希表进行频繁的写入操作。
  4. 并发访问:在渐进式rehash期间,Redis会同时维护原始哈希表和新哈希表。每次访问哈希表时,Redis会同时在两个哈希表中查找数据,以保证数据的一致性。这样可以避免数据丢失或不一致的情况。
  5. 迁移完成:当所有数据都成功迁移到新哈希表中后,渐进式rehash过程完成。此时,Redis会将新哈希表设置为当前的活动哈希表,原始哈希表不再使用,最终会被释放掉

        在Rehash过程中,Redis会逐步将原来哈希表中的键值对迁移到新的哈希表中,直到所有键值对都迁移完成。在这个过程中,Redis仍然可以处理客户端的读写请求,但可能会有一些额外的CPU和内存消耗。 Rehash操作是在后台进行的,不会阻塞Redis的正常操作。在Rehash过程中,如果有新的写操作发生,Redis会同时将新的键值对写入原来的哈希表和新的哈希表中,以保证数据的一致性。 需要注意的是,Rehash操作可能会导致Redis的内存使用量短暂地增加,因为在Rehash过程中同时存在原来的哈希表和新的哈希表。一旦Rehash完成,Redis会释放原来哈希表的内存空间。 总结起来,Redis的Rehash是指在哈希表扩容或缩容时重新计算和重新分配哈希槽的过程,用于提供更多或更少的槽位来存储键值对。Rehash操作是在后台进行的,不会阻塞Redis的正常操作。

二、疑问

2.1 新哈希槽和原始哈希槽中的key是如何对应的

解答:在渐进式rehash算法中,新哈希槽和原始哈希槽中的key是通过哈希函数计算得出的哈希值来进行对应的。 具体的对应过程如下:

  1. 原始哈希槽:初始时,所有的key都存储在原始哈希槽中。每个key通过哈希函数计算出一个哈希值,然后根据哈希值对应到原始哈希槽的一个位置。
  2. 新哈希槽:在进行渐进式rehash时,会逐步创建新的哈希槽。新哈希槽的大小通常是原始哈希槽的两倍。同样,每个key也通过哈希函数计算出一个哈希值,然后根据哈希值对应到新哈希槽的一个位置。
  3. 数据迁移:在渐进式rehash过程中,会逐步将原始哈希槽中的key迁移到新哈希槽中。当需要进行数据迁移时,会根据每个key的哈希值,确定其在新哈希槽中的位置,并将其从原始哈希槽移动到新哈希槽的相应位置。
  4. 对应关系:通过哈希函数计算出的哈希值,决定了每个key在原始哈希槽和新哈希槽中的位置。当进行数据访问时,会根据key的哈希值,确定其在原始哈希槽或新哈希槽中的位置,从而找到对应的数据。

需要注意的是,在渐进式rehash过程中,可能会存在一段时间内同时存在原始哈希槽和新哈希槽中的key。这是为了确保数据的平滑迁移和一致性。随着数据的迁移完成,最终所有的key都会在新哈希槽中找到对应的位置。

2.2 如果同时存在原始哈希槽和新哈希槽中的key,并发访问时如何选择呢

解答:在同时存在原始哈希槽和新哈希槽的情况下,并发访问可能会同时访问到新哈希槽和原始哈希槽中的key。在这种情况下,具体的选择取决于系统的实现和算法。

数据迁移策略:

  • 逐步迁移:可以逐步将原始哈希槽中的数据迁移到新哈希槽中,直到所有数据都迁移完成。可以根据系统负载和性能需求,控制迁移速度和并发度。
  • 批量迁移:可以将原始哈希槽中的数据批量迁移到新哈希槽中,以减少迁移的次数和开销。可以根据系统的负载和数据量,设置合适的批量大小。

访问优先级规则:

  • 优先访问新哈希槽:可以设置优先访问新哈希槽中的数据,以加快新数据的访问速度。可以根据业务需求和数据迁移的进度,调整访问优先级规则。
  • 均衡访问:可以平均分配访问请求到原始哈希槽和新哈希槽中,以保持整体的负载均衡。可以根据系统的负载情况和性能需求,调整访问均衡策略。

同时,在渐进式rehash期间,Redis Cluster会执行以下并发控制机制:

  • 数据迁移:当进行数据迁移时,Redis Cluster会确保在迁移过程中数据的一致性。它使用了一种类似于2PC(两阶段提交)的机制来保证迁移的原子性和一致性。迁移过程中,源节点和目标节点会进行协同操作,确保数据的正确迁移和复制。这样可以避免数据丢失或重复复制的问题。
  • 槽分配:在渐进式rehash期间,Redis Cluster会动态调整数据槽的分配。它使用了一种基于Gossip协议的分布式一致性算法,通过节点之间的通信和协商,确保数据槽的分配在整个集群中保持一致。这样可以避免数据丢失或重复复制的问题,并保证数据的一致性。
  • 客户端路由:在渐进式rehash期间,Redis Cluster会根据槽分配的变化,调整客户端的路由策略。客户端会根据新的槽分配信息,将请求发送到正确的节点上。这样可以确保客户端的请求在整个rehash过程中都能正确地访问到数据,并保持一致性。

2.3. 假设是在rehash过程中删除某个key呢?会怎么样操作

解答:如果要删除某个key,渐进式rehash算法并不会对此产生影响。删除操作不需要进行数据迁移,因此可以直接在原始哈希表中执行删除操作,而不涉及新哈希表。 具体的删除操作如下:

  1. 在原始哈希表中查找要删除的key。
  2. 如果找到了该key,直接删除它。
  3. 如果没有找到该key,说明它可能已经被迁移到新哈希表中,此时需要在新哈希表中查找并删除。

在删除操作中,渐进式rehash算法会同时维护原始哈希表和新哈希表,以确保数据的一致性。删除操作可以在两个哈希表中进行查找和删除,以保证数据的正确性。 需要注意的是,删除操作可能会导致哈希表的负载不均衡。如果删除操作频繁且集中在某个区域,可能会导致哈希表的负载不均衡,需要根据实际情况进行调整和优化,以确保系统的性能和可用性。

2.4 上面说了,迁移过程中,可能会存在一段时间内同时存在原始哈希槽和新哈希槽中的key,假设对某个key的值进行修改,新哈希槽和旧哈希槽的值都会改变吗

解答:在Redis Cluster的迁移过程中,如果对某个key的值进行修改,新哈希槽和旧哈希槽的值都可能会发生改变。这是因为迁移过程中,Redis Cluster会根据新的槽分配信息将数据从旧节点迁移到新节点,同时更新相关的哈希槽信息。 具体来说,如果对某个key的值进行修改,可能会发生以下情况:

  • 如果key的哈希槽在迁移过程中仍然属于旧节点的范围内,那么修改操作会在旧节点上进行,并且新节点的哈希槽信息不会改变。
  • 如果key的哈希槽在迁移过程中已经被迁移到新节点,那么修改操作会在新节点上进行,并且新节点的哈希槽信息会更新。

在任何情况下,Redis Cluster会确保数据的一致性和可用性。如果在迁移过程中对某个key的值进行修改,Redis Cluster会根据哈希槽信息将修改操作正确地路由到相应的节点上,以保证数据的正确性。

2.5 如果在一段时间内旧节点和新节点都存在相同的数据副本,并且访问优先级规则是均衡的,那不就出现两次访问数据不一致了吗

解答:如果一个客户端在迁移过程中先访问了旧节点,然后再访问了新节点,确实会导致数据不一致的情况,因为旧节点和新节点上的数据副本可能不同步。 为了解决这个问题,Redis Cluster引入了客户端的重定向机制。当客户端请求到达一个旧节点,而该节点的数据已经迁移到了新节点时,旧节点会返回一个MOVED重定向错误,指示客户端重新发送请求到新节点。这样可以确保客户端的请求始终路由到正确的节点上,保证数据的一致性。 此外,Redis Cluster还提供了ASK重定向机制,用于处理正在进行迁移的哈希槽。ASK重定向机制类似于MOVED,但会告诉客户端该哈希槽正在迁移中,客户端可以根据ASK重定向错误重新发送请求到新节点。 综上所述,虽然在迁移过程中可能存在一段时间内旧节点和新节点都存在相同的数据副本,但通过Redis Cluster的重定向机制,可以确保客户端的请求始终路由到正确的节点上,避免数据不一致的问题。

2.6 重试case

        客户端需要手动实现一个重定向处理器,并需要考虑以下几点:

  • 重试次数限制:为了避免无限重试,可以设置一个重试次数限制,超过限制后可以选择放弃请求或进行其他处理。
  • 错误处理策略:除了重试,还可以根据具体的业务需求,选择其他错误处理策略,例如记录日志、回滚操作等。
  • 高可用性和容错性:为了提高系统的高可用性和容错性,可以在客户端实现多个节点的故障转移和自动重试,以应对节点故障和重定向错误。

例如,使用Redisson来实现:Redisson重定向默认限制为16次,若超出,会抛出 RedisConnectionException 异常,指示重定向次数过多

import org.redisson.Redisson;
import org.redisson.api.*;
import org.redisson.api.exception.RedissonMovedException;
import org.redisson.api.exception.RedissonAskException;public class RedissonClusterExample {public static void main(String[] args) {Config config = new Config();config.useClusterServers().addNodeAddress("redis://127.0.0.1:7000", "redis://127.0.0.1:7001").setRedirectionHandler(new RedissonRedirectionHandler() {@Overridepublic void handle(String host, int port, RedisMovedException e) {// 处理MovedDataExceptionSystem.out.println("Handling MovedDataException");// 更新Redisson配置以连接到新的节点config.useClusterServers().addNodeAddress("redis://" + host + ":" + port);}@Overridepublic void handle(String host, int port, RedisAskException e) {// 处理AskDataExceptionSystem.out.println("Handling AskDataException");// 更新Redisson配置以连接到新的节点config.useClusterServers().addNodeAddress("redis://" + host + ":" + port);}});RedissonClient redisson = Redisson.create(config);RMap<String, String> map = redisson.getMap("myMap");map.put("key", "value");redisson.shutdown();}
}

相关文章:

  • openssl+ DES开发实例(Linux)
  • Linux 系统编程,Binder 学习,文件访问相关的接口
  • 【BIM入门实战】Revit图元的选择方式,总有一款适合你
  • JAXB实现XML和Bean相互转换
  • MyBatis的插件能在哪些地方进行拦截?
  • flutter开发web应用支持浏览器跨域设置
  • RobustVideoMatting 预测图片
  • centos 6.10 安装 svn1.14.2
  • 自己动手实现一个深度学习算法——六、与学习相关的技巧
  • 【matlab】KMeans KMeans++实现手写数字聚类
  • 集成Line、Facebook、Twitter、Google、微信、QQ、微博、支付宝的三方登录sdk
  • 开启创造力之门:掌握Vue中Slot插槽的使用技巧与灵感
  • Intellij IDEA 内存设置的问题 及解决
  • Python高级语法----Python C扩展与性能优化
  • windiws docker 部署jar window部署docker 转载
  • 《剑指offer》分解让复杂问题更简单
  • 【RocksDB】TransactionDB源码分析
  • 【前端学习】-粗谈选择器
  • canvas绘制圆角头像
  • EventListener原理
  • GDB 调试 Mysql 实战(三)优先队列排序算法中的行记录长度统计是怎么来的(上)...
  • java B2B2C 源码多租户电子商城系统-Kafka基本使用介绍
  • JavaScript中的对象个人分享
  • Java反射-动态类加载和重新加载
  • Leetcode 27 Remove Element
  • Node + FFmpeg 实现Canvas动画导出视频
  • tweak 支持第三方库
  • vue--为什么data属性必须是一个函数
  • vue学习系列(二)vue-cli
  • 测试开发系类之接口自动化测试
  • 高程读书笔记 第六章 面向对象程序设计
  • 模仿 Go Sort 排序接口实现的自定义排序
  • ​力扣解法汇总946-验证栈序列
  • #include到底该写在哪
  • #pragma once
  • #周末课堂# 【Linux + JVM + Mysql高级性能优化班】(火热报名中~~~)
  • (1) caustics\
  • (Bean工厂的后处理器入门)学习Spring的第七天
  • (安全基本功)磁盘MBR,分区表,活动分区,引导扇区。。。详解与区别
  • (附源码)springboot 房产中介系统 毕业设计 312341
  • (附源码)计算机毕业设计ssm电影分享网站
  • (九)c52学习之旅-定时器
  • (求助)用傲游上csdn博客时标签栏和网址栏一直显示袁萌 的头像
  • (转)MVC3 类型“System.Web.Mvc.ModelClientValidationRule”同时存在
  • *p=a是把a的值赋给p,p=a是把a的地址赋给p。
  • .NET CF命令行调试器MDbg入门(三) 进程控制
  • .net framework 4.0中如何 输出 form 的name属性。
  • .NET Framework与.NET Framework SDK有什么不同?
  • .net wcf memory gates checking failed
  • .NET/C# 使用 SpanT 为字符串处理提升性能
  • .NET8.0 AOT 经验分享 FreeSql/FreeRedis/FreeScheduler 均已通过测试
  • .netcore如何运行环境安装到Linux服务器
  • .NET框架设计—常被忽视的C#设计技巧
  • @Controller和@RestController的区别?
  • [ vulhub漏洞复现篇 ] GhostScript 沙箱绕过(任意命令执行)漏洞CVE-2019-6116