乐趣区

关于java:为什么要用Redis集群

面试官 聊下 Redis 的分片集群,先聊 Redis Cluster 好咯?

面试官Redis Cluser 是 Redis 3.x 才有的官网集群计划,这块你理解多少?

候选者:嗯,要不还是从根底讲起呗?

候选者:在后面聊 Redis 的时候,提到的 Redis 都是「单实例」存储所有的数据。

候选者:1. 主从模式下实现读写拆散的架构,能够让多个从服务器承载「读流量」,但面对「写流量」时,始终是只有主服务器在抗。

候选者:2.「纵向扩大」降级 Redis 服务器硬件能力,但降级至肯定水平下,就不划算了。

候选者:纵向扩大意味着「大内存」,Redis 长久化时的”老本”会加大(Redis 做 RDB 长久化,是全量的,fork 子过程时有可能因为应用内存过大,导致主线程阻塞工夫过长)

候选者:所以,「单实例」是有瓶颈的

候选者:「纵向扩大」不行,就「横向扩大」呗。

候选者:用多个 Redis 实例来组成一个集群,依照肯定的规定把数据「散发」到不同的 Redis 实例上。当集群所有的 Redis 实例的数据加起来,那这份数据就是全的

候选者:其实就是「分布式」的概念(:只不过,在 Redis 里,如同叫「分片集群」的人比拟多?

候选者:从后面就得悉了,要「分布式存储」,就必定防止不了对数据进行「散发」(也是路由的意思)

候选者:从 Redis Cluster 讲起吧,它的「路由」是做在客户端的(SDK 曾经集成了路由转发的性能)

候选者 :Redis Cluster 对数据的散发的逻辑中,波及到「哈希槽」(Hash Solt) 的概念

候选者:Redis Cluster 默认一个集群有 16384 个哈希槽,这些哈希槽会调配到不同的 Redis 实例中

候选者:至于怎么「瓜分」,能够间接均分,也能够「手动」设置每个 Redis 实例的哈希槽,全由咱们来决定

候选者:重要的是,咱们要把这 16384 个都得瓜分完,不能有残余!

候选者:当客户端有数据进行写入的时候,首先会对 key 依照 CRC16 算法计算出 16bit 的值(能够了解为就是做 hash),而后失去的值对 16384 进行取模

候选者:取模之后,天然就失去其中一个哈希槽,而后就能够将数据插入到调配至该哈希槽的 Redis 实例中

面试官 那问题就来了,当初客户端通过 hash 算法算出了哈希槽的地位,那客户端怎么晓得这个哈希槽在哪台 Redis 实例上呢?

候选者:是这样的,在集群的中每个 Redis 实例都会向其余实例「流传」本人所负责的哈希槽有哪些。这样一来,每台 Redis 实例就能够记录着「所有哈希槽与实例」的关系了(:

候选者:有了这个映射关系当前,客户端也会「缓存」一份到本人的本地上,那天然客户端就晓得去哪个 Redis 实例上操作了

面试官 那我又有问题了,在集群里也能够新增或者删除 Redis 实例啊,这个怎么整?

候选者:当集群删除或者新增 Redis 实例时,那总会有某 Redis 实例所负责的哈希槽关系会发生变化

候选者:发生变化的信息会通过音讯发送至整个集群中,所有的 Redis 实例都会晓得该变动,而后更新本人所保留的映射关系

候选者:但这时候,客户端其实是不感知的(:

候选者:所以,当客户端申请时某 Key 时,还是会申请到「原来」的 Redis 实例上。而原来的 Redis 实例会返回「moved」命令,通知客户端应该要去新的 Redis 实例下来申请啦

候选者:客户端接管到「moved」命令之后,就晓得去新的 Redis 实例申请了,并且更新「缓存哈希槽与实例之间的映射关系」

候选者:总结起来就是:数据迁徙结束后被响应,客户端会收到「moved」命令,并且会更新本地缓存

面试官 那数据还没齐全迁徙完呢?

候选者:如果数据还没齐全迁徙完,那这时候会返回客户端「ask」命令。也是让客户端去申请新的 Redis 实例,但客户端这时候不会更新本地缓存

面试官:理解了

面试官:说白了就是,如果集群 Redis 实例存在变动,因为 Redis 实例之间会「通信」

面试官:所以等到客户端申请时,Redis 实例总会晓得客户端所要申请的数据在哪个 Redis 实例上

面试官:如果曾经迁徙结束了,那就返回「move」命令通知客户端应该去找哪个 Redis 实例要数据,并且客户端应该更新本人的缓存(映射关系)

面试官:如果正在迁徙中,那就返回「ack」命令通知客户端应该去找哪个 Redis 实例要数据

候选者:不愧是你…

面试官 那你晓得为什么哈希槽是 16384 个吗?

候选者:嗯,这个。是这样的,Redis 实例之间「通信」会相互交换「槽信息」,那如果槽过多(意味着网络包会变大),网络包变大,那是不是就意味着会「适度占用」网络的带宽

候选者:另外一块是,Redis 作者认为集群在个别状况下是不会超过 1000 个实例

候选者:那就取了 16384 个,即能够将数据正当打散至 Redis 集群中的不同实例,又不会在替换数据时导致带宽占用过多

面试官:理解了

面试官 那你晓得为什么对数据进行分区在 Redis 中用的是「哈希槽」这种形式吗?而不是一致性哈希算法

候选者:在我了解下,一致性哈希算法就是有个「哈希环」,当客户端申请时,会对 Key 进行 hash,确定在哈希环上的地位,而后顺时针往后找,找到的第一个实在节点

候选者:一致性哈希算法比「传统固定取模」的益处就是:如果集群中须要新增或删除某实例,只会影响一小部分的数据

候选者:但如果在集群中新增或者删除实例,在一致性哈希算法下,就得晓得是「哪一部分数据」受到影响了,须要进行对受影响的数据进行迁徙

面试官:嗯…

候选者:而哈希槽的形式,咱们通过下面曾经能够发现:在集群中的每个实例都能拿到槽位相干的信息

候选者:当客户端对 key 进行 hash 运算之后,如果发现申请的实例没有相干的数据,实例会返回「重定向」命令通知客户端应该去哪儿申请

候选者:集群的扩容、缩容都是以「哈希槽」作为根本单位进行操作,总的来说就是「实现」会更加简略(简洁,高效,有弹性)。过程大略就是把局部槽进行重新分配,而后迁徙槽中的数据即可,不会影响到集群中某个实例的所有数据。

面试官 那你理解「服务端 路由」的大抵原理吗?

候选者:嗯,服务端路由个别指的就是,有个代理层专门对接客户端的申请,而后再转发到 Redis 集群进行解决

候选者:上次最初面试的时候,也提到了,当初比拟风行的是 Codis

候选者:它与 Redis Cluster 最大的区别就是,Redis Cluster 是直连 Redis 实例的,而 Codis 则客户端直连 Proxy,再由 Proxy 进行散发到不同的 Redis 实例进行解决

候选者:在 Codis 对 Key 路由的计划跟 Redis Cluster 很相似,Codis 初始化出 1024 个哈希槽,而后调配到不同的 Redis 服务器中

候选者:哈希槽与 Redis 实例的映射关系由 Zookeeper 进行存储和治理,Proxy 会通过 Codis DashBoard 失去最新的映射关系,并缓存在本地上

面试官 那如果我要扩容 Codis Redis 实例的流程是怎么样的?

候选者:简略来说就是:把新的 Redis 实例退出到集群中,而后把局部数据迁徙到新的实例上

候选者:大略的过程就是:1.「原实例」某一个 Solt 的局部数据发送给「指标实例」。2.「指标实例」收到数据后,给「原实例」返回 ack。3.「原实例」收到 ack 之后,在本地删除掉刚刚给「指标实例」的数据。4. 一直循环 1、2、3 步骤,直至整个 solt 迁徙结束

候选者:Codis 也是反对「异步迁徙」的,针对下面的步骤 2,「原实例」发送数据后,不期待「指标实例」返回 ack,就持续接管客户端的申请。

候选者:未迁徙完的数据标记为「只读」,不会影响到数据的一致性。如果对迁徙中的数据存在「写操作」,那会让客户端进行「重试」,最初会写到「指标实例」上

候选者:还有就是,针对 bigkey,异步迁徙采纳了「拆分指令」的形式进行迁徙,比方有个 set 元素有 10000 个,那「原实例」可能就发送 10000 条命令给「指标实例」,而不是一整个 bigkey 一次性迁徙(因为大对象容易造成阻塞)

面试官:理解了。

本文总结

  • 分片集群诞生理由:写性能在高并发下会遇到瓶颈 && 无奈有限地纵向扩大(不划算)
  • 分片集群:须要解决「数据路由」和「数据迁徙」的问题
  • Redis Cluster 数据路由

    • Redis Cluster 默认一个集群有 16384 个哈希槽,哈希槽会被调配到 Redis 集群中的实例中
    • Redis 集群的实例会互相「通信」,交互本人所负责哈希槽信息(最终每个实例都有残缺的映射关系)
    • 当客户端申请时,应用 CRC16 算法算出 Hash 值并模以 16384,天然就能失去哈希槽进而失去所对应的 Redis 实例地位
  • 为什么 16384 个哈希槽:16384 个既能让 Redis 实例调配到的数据绝对平均,又不会影响 Redis 实例之间交互槽信息产生重大的网络性能开销问题
  • Redis Cluster 为什么应用哈希槽,而非一致性哈希算法:哈希槽实现绝对简略高效,每次扩缩容只须要动对应 Solt(槽)的数据,个别不会动整个 Redis 实例
  • Codis 数据路由:默认调配 1024 个哈希槽,映射相干信息会被保留至 Zookeeper 集群。Proxy 会缓存一份至本地,Redis 集群实例发生变化时,DashBoard 更新 Zookeeper 和 Proxy 的映射信息
  • Redis Cluster 和 Codis 数据迁徙:Redis Cluster 反对同步迁徙,Codis 反对同步迁徙 && 异步迁徙

    • 把新的 Redis 实例退出到集群中,而后把局部数据迁徙到新的实例上(在线)

欢送关注我的微信公众号【Java3y】来聊聊 Java 面试,对线面试官系列继续更新中!

【对线面试官 - 挪动端】系列 一周两篇继续更新中!

【对线面试官 - 电脑端】系列 一周两篇继续更新中!

原创不易!!求三连!!

退出移动版