rehash和reshard有什么区别?

首先redis的定位是内存数据库,并支持集群模式,cluster模式的意义就是支持在分布式环境中,通过增加机器节点,水平扩展redis的存储和读取性能。
所以这里存在两种扩容,一种是单机内的内存扩容,一种是分布式集群的节点扩容。前者是rehash,后者是reshard。后者在很多支持分布式的组件中都存在,也就是数据重平衡,如rocketmq、mongodb、kafka。

什么时候触发rehash?

rehash的过程是redis自动触发的,当以下条件中的任意一个被满足时,程序会自动开始对哈希表执行扩展操作:

  • 服务器目前没有在执行BGSAVE命令或者BGREWRITEAOF命令,并且哈希表的负载因子大于等于1。
  • 服务器目前正在执行BGSAVE命令或者BGREWRITEAOF命令,并且哈希表的负载因子大于等于5。
  • 另一方面,当哈希表的负载因子小于0.1时,程序自动开始对哈希表执行收缩操作。

负载因子 = 哈希表已保存节点数量 / 哈希表大小,那为什么可以大于1呢,显然和hashmap一样,哈希下标冲突后会基于链表方式进行处理。
BGSAVE和BGREWRITEAOF命令我们都知道,分别是触发rdb和aof两种持久化方式的,那为什么持久化过程中就将负载因子条件设置这么大呢,显然越大越不容易触发扩容,结合起来就是说redis不希望在持久化过程中扩容。原因详见:Redis的RDB持久化过程。总结来说,由于扩容过程中的,读操作也会触发写操作,而持久化过程中写操作会触发COW(copy on write)增大写放大消耗内存。在Redis 满容状态下由于Rehash会导致大量Key驱逐。

rehash的过程?

在Redis中,键值对(Key-Value Pair)存储方式是由字典(Dict)保存的,而字典底层是通过哈希表来实现的。通过哈希表中的节点保存字典中的键值对。在字典内部,维护了两张哈希表。 一般情况下, 字典只使用 ht[0] 哈希表, ht[1] 哈希表只会在对 ht[0] 哈希表进行 rehash 时使用。步骤如下:

  1. 为 ht[1] 分配空间, 让字典同时持有 ht[0] 和 ht[1] 两个哈希表。
  2. 在字典中维持一个索引计数器变量 rehashidx , 并将它的值设置为 0 , 表示 rehash 工作正式开始。
  3. 在 rehash 进行期间, 每次对字典执行添加、删除、查找或者更新操作时, 程序除了执行指定的操作以外, 还会顺带将 ht[0] 哈希表在 rehashidx 索引上的所有键值对 rehash 到 ht[1] , 当 rehash 工作完成之后, 程序将 rehashidx 属性的值增一。
  4. 随着字典操作的不断执行, 最终在某个时间点上, ht[0] 的所有键值对都会被 rehash 至 ht[1] , 这时程序将 rehashidx 属性的值设为 -1 , 表示 rehash 操作已完成。

渐进式 rehash 的好处在于它采取分而治之的方式, 将 rehash 键值对所需的计算工作均滩到对字典的每个添加、删除、查找和更新操作上, 从而避免了集中式 rehash 而带来的庞大计算量。
虽然渐进式rehash避免了redis阻塞,但是由于在rehash时需要分配一个新的hash表,在rehash期间,同时有两个hash表在使用,会使得redis内存使用量瞬间突增,在Redis 满容状态下由于Rehash会导致大量Key驱逐。

什么时候触发reshard?

数据再分片需要人工判断,基于对现有redis的使用容量和读写负载的判断,决定是否在在已有节点内部进行哈希槽的重新划分,以及是否需要扩缩节点。

reshard的过程?

  1. redis-cli --cluster add-node 192.168.11.135:6379 192.168.11.131:6379:增加主节点,第一个参数是新节点的地址,第二个参数是任意一个已经存在的节点的IP和端口。然后开始reshard。
  2. redis-cli --cluster del-node 192.168.11.131:6379 6ac62a:第一个参数是任意一个节点的地址,第二个节点是你想要移除的节点地址。在移除主节点前需要确保这个主节点是空的,如果不是空的需要将这个节点的数据reshard到其他主节点上。
  3. 所以不管是扩缩容,都会要走到对内部已有节点重新划分哈希槽,我们来看步骤:
    1. redis-cli cluster info:获取集群节点信息,用于判断节点数量情况。
    2. redis-cli --cluster check 192.168.11.131:6379:获取哈希槽分布信息,用于判断需要将哪些节点的哪些槽分配到哪些新节点上。
    3. redis-cli --cluster reshard 192.168.11.131:6379 --cluster-from 6ac62a --cluster-to 9026f2 --cluster-slots 3276 --cluster-yes:参数就不解释了,很容易看懂,就是将某个节点的某些哈希槽迁移到指定的节点上。
    4. redis-cli --cluster rebalance 192.168.11.135:6379 --cluster-use-empty-masters:根据所有节点数量,自动将所有的哈希槽进行重新平衡分配。注意要带上cluster-use-empty-masters配置,否则不会将空节点(通常情况是刚加入的节点)纳入平衡范围。
    5. 这里可能要问reshard和rebalance两个命令有什么区别?首先他们的功能确实都是将哈希槽重新分配(哈希槽重新分配就是指包括槽里面的key和value),区别在于reshard可以完全自定义,rebalance就是自动且强制平衡,比如要删除前需要清空一个节点的槽,那么就只能用reshard了。又比如增加节点后如果需要保持槽平衡需要操作很多次reshard才能实现的话,就可以直接用rebalance非常方便。

      其他注意事项

  • reshard并不会对正在运行的集群程序产生任何影响。
  • 集群管理工具redis-trib.rb已经被废弃,所以不用安装ruby啥的了,当时redis-trib.rb的功能,现在已经集成到了redis-cli中,并且可以在有认证的情况执行了,可以通过./redis-cli —cluster help查看使用方式。
  • 过程可以参考:。