大厂面试官问我:为什么Redis的rehash采用渐进式,而Java的hashmap是一次性rehash?【后端八股文十二:Redis hash八股文合集】

 本文为【Redis hash八股文合集】初版,后续还会进行优化更新,欢迎大家关注交流~

hello hello~ ,这里是绝命Coding——老白~💖💖 ,欢迎大家点赞🥳🥳关注💥💥收藏🌹🌹🌹
19d95742d45b4220ad0ae0359ffcba93.png

💥个人主页绝命Coding-CSDN博客
💥 所属专栏后端技术分享
这里将会不定期更新有关后端、前端的内容,希望大家多多点赞关注收藏💖

    往期内容(篇幅过多,不一一列出,感兴趣的小伙伴可以查看专栏):

大厂面试官问我:Redis处理点赞,如果瞬时涌入大量用户点赞(千万级),应当如何进行处理?【后端八股文一:Redis点赞八股文合集】-CSDN博客

大厂面试官问我:布隆过滤器有不能扩容和删除的缺陷,有没有可以替代的数据结构呢?【后端八股文二:布隆过滤器八股文合集】-CSDN博客

hash


相当于Java的HashMap,无序字典,内部实现结构上同Java的HashMap一致,也是  数组+链表
hash冲突就会用链表

redis中hash如果哈希冲突多,有什么办法改进
  1. 调整哈希函数: Redis 默认使用 MurmurHash2 算法作为哈希函数,这个算法在大多数情况下都能提供良好的散列分布。但如果发现特定的数据模式导致严重的哈希冲突,可以尝试使用其他哈希算法,比如 SHA-1 或 CRC32。
  2. 增大 hash table 的大小: Redis 的 hash table 是动态调整大小的,当冲突过多时可以显式地调用 HRESIZE 命令来增大 hash table 的大小,从而减少冲突。
  3. 使用 Redis 集群: Redis 集群模式下,key 会根据哈希值被自动分布到不同的节点上,有效地缓解了单机 hash table 的冲突问题。

Redis中hashmap数据类型的相关方法?
  • HSET/HMSET: 设置 hash field 的值
  • HGET/HMGET: 获取 hash field 的值
  • HDEL: 删除 hash field
  • HLEN: 获取 hash 包含的 field 数量
  • HKEYS/HVALS: 获取所有 field 名称或值
  • HGETALL: 获取整个 hash 的所有 field 和值

Redis中hashmap类型的大键在集群情况下是否在多台机器上,还是单台机器上?
  • 在 Redis 集群模式下,hash 的大 key 会根据 CRC16 hash 算法被分配到不同的分片(slot)上,也就是分散在不同的节点上。
  • 这样可以更好地利用集群的存储和计算能力,提高整体的吞吐量和可扩展性。
  • 对于单机 Redis,hash 的大 key 则都存储在同一台机器上。

Redis的哈希底层?

Redis 的哈希底层使用了一种叫做 "dict" 的哈希表数据结构来实现。

"dict" 由两个哈希表组成,主哈希表和备用哈希表。主哈希表用于存储数据,备用哈希表主要用于 rehash 操作。

每个哈希表由一个 dictht 结构体表示,包含了数组、已使用的槽数量、总槽数量等属性。

哈希算法使用 MurmurHash2,可以根据需要切换到其他算法如 SipHash。

Redis hash怎么存, 哈希冲突 扩容, 查询流程

(1)存储

  • Redis 的 hash 数据类型底层使用 dict 结构来存储 key-value 对。
  • 每个 hash 对应一个 dict,key 为 hash field,value 为对应的值。
  • 当 hash 的 field 过多时,会自动扩容 dict 以减少冲突。

(2)解决哈希冲突

  • Redis 使用拉链法来解决哈希冲突,即一个槽位下挂载一个链表存储所有冲突的键值对。
  • 当冲突过多时,会自动对主哈希表进行 rehash 操作,把数据迁移到更大的哈希表中

(3)哈希表的扩容

  • rehash 分为两个阶段:渐进式 rehash 和自动 rehash。
  • 渐进式 rehash 会在每次查询/更新时,将一部分数据迁移到新的哈希表中。
  • 当渐进式 rehash 完成后,会自动切换到新的哈希表,旧表被删除。

(4)查询流程

  • 查询时,先根据 key 计算出 hash 值,定位到主哈希表的对应槽位。
  • 遍历槽位下的链表,逐个比对 field 名称找到对应的值。
  • 如果正在执行 rehash,还需要同时查询备用哈希表。
Redis为什么要这么用渐进式哈希来扩容
  • 一次性 rehash 会导致短暂的服务中断,因为在 rehash 完成之前,无法处理任何请求。
  • 渐进式 rehash 可以将 rehash 过程拆分成多个步骤,在处理正常请求的同时,逐步迁移数据到新的哈希表。
  • 这样可以大大减少 rehash 过程对服务的影响,提高系统的可用性。

redis的hash渐进式扩容的触发条件
  • Redis 会监控哈希表的负载因子(used_slots / total_slots),当主哈希表的负载因子超过阈值(1.0)时,会开始执行渐进式 rehash。
  • 具体的阈值可以通过 hash-max-ziplist-entrieshash-max-ziplist-value 配置参数来控制。
  • 除了负载因子,Redis 也会根据当前的内存使用情况来决定是否需要触发 rehash。

渐进式 rehash 的过程
  • Redis 会在处理每个请求时,顺带将主哈希表中的一个 entry 迁移到备用哈希表中。
  • 这样可以将 rehash 过程分散到每个请求中,减少对服务的影响。
  • 当备用哈希表的 entry 数量超过主哈希表的 50% 时,Redis 会切换到使用备用哈希表作为新的主哈希表。

Redis渐进式rehash过程中,每处理⼀个请求时,从哈希表1中的第 ⼀个索引位置开始,顺带着将这个索引位置上的所有entries拷贝到哈希表2中,这个请求是读请求还是写请求?

这个请求可以是读请求,也可以是写请求

无论是读请求还是写请求,只要涉及到访问 hash 数据结构,Redis 都会进行这个渐进式迁移的动作。

如果渐进式时,这些的key突然都不访问了 会有什么问题
  • 如果 hash 中的 key 在 rehash 期间完全没有访问,那么 rehash 的进度会非常缓慢。
  • 为了解决这个问题,Redis 会在处理查询/更新请求时,优先将对应 key 所在槽位的数据迁移到新哈希表。
  • 这样可以确保热点数据能够尽快完成迁移,提高 rehash 的效率。

为啥redis的rehash采用渐进式,而hashmap是一次性rehash
  • Java 的 HashMap 采用一次性 rehash 是因为它是单机内存数据结构,不需要考虑服务可用性。
  • 而 Redis 作为分布式缓存,需要在保证服务高可用的同时,完成动态扩容,所以采用了渐进式的 rehash 机制。
Redis中hashmap类型的大键在集群情况下是否在多台机器上,还是单台机器上?
  • 在 Redis 集群模式下,hash 的大 key 会根据 CRC16 hash 算法被分配到不同的分片(slot)上,也就是分散在不同的节点上。
  • 这样可以更好地利用集群的存储和计算能力,提高整体的吞吐量和可扩展性。
  • 对于单机 Redis,hash 的大 key 则都存储在同一台机器上。

Redis的哈希底层?

Redis 的哈希底层使用了一种叫做 "dict" 的哈希表数据结构来实现。

"dict" 由两个哈希表组成,主哈希表和备用哈希表。主哈希表用于存储数据,备用哈希表主要用于 rehash 操作。

每个哈希表由一个 dictht 结构体表示,包含了数组、已使用的槽数量、总槽数量等属性。

哈希算法使用 MurmurHash2,可以根据需要切换到其他算法如 SipHash。

Redis hash怎么存, 哈希冲突 扩容, 查询流程

(1)存储

  • Redis 的 hash 数据类型底层使用 dict 结构来存储 key-value 对。
  • 每个 hash 对应一个 dict,key 为 hash field,value 为对应的值。
  • 当 hash 的 field 过多时,会自动扩容 dict 以减少冲突。

(2)解决哈希冲突

  • Redis 使用拉链法来解决哈希冲突,即一个槽位下挂载一个链表存储所有冲突的键值对。
  • 当冲突过多时,会自动对主哈希表进行 rehash 操作,把数据迁移到更大的哈希表中

(3)哈希表的扩容

  • rehash 分为两个阶段:渐进式 rehash 和自动 rehash。
  • 渐进式 rehash 会在每次查询/更新时,将一部分数据迁移到新的哈希表中。
  • 当渐进式 rehash 完成后,会自动切换到新的哈希表,旧表被删除。

(4)查询流程

  • 查询时,先根据 key 计算出 hash 值,定位到主哈希表的对应槽位。
  • 遍历槽位下的链表,逐个比对 field 名称找到对应的值。
  • 如果正在执行 rehash,还需要同时查询备用哈希表。
Redis为什么要这么用渐进式哈希来扩容
  • 一次性 rehash 会导致短暂的服务中断,因为在 rehash 完成之前,无法处理任何请求。
  • 渐进式 rehash 可以将 rehash 过程拆分成多个步骤,在处理正常请求的同时,逐步迁移数据到新的哈希表。
  • 这样可以大大减少 rehash 过程对服务的影响,提高系统的可用性。

redis的hash渐进式扩容的触发条件
  • Redis 会监控哈希表的负载因子(used_slots / total_slots),当主哈希表的负载因子超过阈值(1.0)时,会开始执行渐进式 rehash。
  • 具体的阈值可以通过 hash-max-ziplist-entrieshash-max-ziplist-value 配置参数来控制。
  • 除了负载因子,Redis 也会根据当前的内存使用情况来决定是否需要触发 rehash。

渐进式 rehash 的过程

(空间换时间的渐进式哈希)

  • Redis 会在处理每个请求时,顺带将主哈希表中的一个 entry 迁移到备用哈希表中。
  • 这样可以将 rehash 过程分散到每个请求中,减少对服务的影响。
  • 当备用哈希表的 entry 数量超过主哈希表的 50% 时,Redis 会切换到使用备用哈希表作为新的主哈希表。

Redis渐进式rehash过程中,每处理⼀个请求时,从哈希表1中的第 ⼀个索引位置开始,顺带着将这个索引位置上的所有entries拷贝到哈希表2中,这个请求是读请求还是写请求?

这个请求可以是读请求,也可以是写请求

无论是读请求还是写请求,只要涉及到访问 hash 数据结构,Redis 都会进行这个渐进式迁移的动作。

如果渐进式时,这些的key突然都不访问了 会有什么问题
  • 如果 hash 中的 key 在 rehash 期间完全没有访问,那么 rehash 的进度会非常缓慢。
  • 为了解决这个问题,Redis 会在处理查询/更新请求时,优先将对应 key 所在槽位的数据迁移到新哈希表。
  • 这样可以确保热点数据能够尽快完成迁移,提高 rehash 的效率。

为啥redis的rehash采用渐进式,而hashmap是一次性rehash
  • Java 的 HashMap 采用一次性 rehash 是因为它是单机内存数据结构,不需要考虑服务可用性。
  • 而 Redis 作为分布式缓存,需要在保证服务高可用的同时,完成动态扩容,所以采用了渐进式的 rehash 机制。

Redis一次性将很多数据从哈希1拷贝至哈希2是否会造成线程阻塞?

不会

Redis是单线程模型,如果一次性将大量数据从一个hash迁移到另一个hash,的确可能会造成线程阻塞,影响Redis的响应和性能。所以Redis引入了渐进式rehash的机制:

  • 在需要rehash时,Redis会为新的hash表分配空间,但不会一次性将所有数据rehash。
  • 每次客户端访问时,Redis会检查是否需要rehash,如果需要则rehash一部分数据。
  • 这样可以将rehash过程拆分,避免一次性rehash导致的线程阻塞。

    后期新的八股文合集文章会继续分享,感兴趣的小伙伴可以点个关注~

 更多精彩内容以及免费资料请关注公众号:绝命Coding

914cbb12b2c3492aaa31232a11aa9c64.png

  • 60
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值