什么是Redis集群?
面试官 :聊下Redis的分片集群,先聊 Redis Cluster好咯?
面试官 :Redis Cluser是Redis 3.x才有的官方集群方案,这块你了解多少?
候选者 :嗯,要不还是从基础讲起呗?
候选者 :在前面聊Redis的时候,提到的Redis都是「单实例」存储所有的数据。
候选者 :1. 在主从架构中实现读写分离的设计可以让多个从服务器负责处理 read 流量,在 handle write 操作时,则仅有主服务器承担 write 操作。
候选者 :2. 「纵向扩展」提升了Redis服务器的性能,并经过一定阶段的提升后不再具有成本效益。
contenders:纵向扩展等同于大内存需求。
Redis采用RDB持久化方案,则表示其可恢复全部数据。
当子进程被分叉时,在内存占用过高可能导致主线程被阻塞。
候选者 :所以,「单实例」是有瓶颈的
[

候选者 :「纵向扩展」不行,就「横向扩展」呗。
候选者:由多个Redis实例构成的一个集群系统,在根据预设的负载均衡策略下将数据分散存储于各个不同的Redis实例上。只有当所有参与该集群的所有Redis实例的数据总和达成一致时才认为这份数据是完整的。
分布式系统:其实就是「分布式」的概念(:不过,在 Redis 社区中常用术语称为「分片式架构」的人比较多?)
[


候选人:之前已经了解过相关信息。如果选择「分布式存储」方案,则不可避免地会进行大量数据的「分发」处理(这也是数据路由的一部分)。
候选人 :让我们一起从Redis Cluster谈谈吧?它的「路由」机制其实是做了客户端层面的处理(其SDK已经集成了路由转发功能)。这样的设计使得数据传输更加高效可靠。
Redis Cluster在数据分发的过程中涉及到了「哈希槽」的概念
** contenders**: Redis Cluster 默认每个集群拥有 16384 个 hash slots,在初始化时会自动将这些 hash slots 分配至各个 Redis 实例。
[

选择者:关于资源划分的方式,则有两种选项:一种是平均分配给各个Redis实例;另一种则是自行配置每个实例的哈希槽数量。
候选者 :重要的是,我们要把这16384个都得瓜分完,不能有剩余!
候选者:当客户端向系统发送需要处理的数据时,在数据传输过程中系统会立即对key值应用CRC16算法进行计算以获取对应的16位数值(即生成哈希值),随后将该数值与16384取模以确定后续的操作步骤。
候选者:执行取模运算后,必然导致得到一个特定的哈希槽;进而可以将数据插入到与该哈希槽关联的Redis实例中。
主持人 :“那么问题来了,请问现在客户端通过使用哈希算法计算确定了哈希槽的位置,请问那么客户端如何确定该哈希槽位于哪台Redis实例上呢?”
在集群内部,每个 Redis 实例都会将负责的所有哈希槽分发给其他 Redis 实例.这样一来,每台 Redis 实例就可以维护其对应的键空间范围被分配到该 Redis 实例的所有哈希槽与各自所属的 Redis 实例之间的关系被维护下来(:
候选人:在建立了该映射关系后,在线的客户端也会「缓存」一份数据到自己的本地存储中去。这样一来,在线的客户端就会相应地知道要访问哪个Redis实例来进行操作。
[

您:我现在遇到了一个问题,在集群里也可以新增或移除Redis实例啊,请问该怎么处理呢?
成员:每当集群删除或创建Redis实例时,相应地,在每次操作后都会影响到某个Redis实例所管理的一组哈希槽关系。
候选节点:当发生变动信息时会触发消息传播机制,在此过程中整个Redis集群中的每一个实例都会收到该变化通知,并相应地更新自身存储的映射数据关联。
候选者 :但这时候,客户端其实是不感知的(:
潜在用户:因此,在客户端发起对该Key的请求时,在线服务仍会将该请求转发至「当前」Redis实例上执行处理。然而,在收到「move」指令后,则应指示客户端将后续相关操作转移至新的Redis实例进行处理
参与者:当客户端响应「moved」命令后, 该系统会指示其前往新的Redis实例进行请求, 并同步更新「缓存哈希槽与新旧Redis实例之间的映射关系」.
候选方案:具体而言是,在数据迁移完成之后系统会做出响应,在线端客户将接收到「moved」命令,并且本地缓存将被更新以反映这一状态
[

面试官 :那数据还没完全迁移完呢?
候选者将被触发,在数据尚未全部迁移完成时会向客户端发送「ask」命令,并引导该客户端连接新的Redis实例;然而,在此期间本地缓存不会被更新
面试官 :了解了
负责人
面试官
面试官人员
面试官应当告知客户端,在进行Redis迁移操作时,请使用「ack」命令指示客户端前往对应的Redis实例获取所需数据
候选者 :不愧是你…
面试官 :那你知道为什么哈希槽是16384个吗?
候选者 :该方案通过通信机制实现数据同步。具体而言,在Redis实例之间通信时, 每个Redis实例都会发送包含槽信息的数据包给其他Redis实例进行同步操作. 当槽数量增加(即每个Redis实例发送的数据量增大)时, 在相同的网络带宽下(即带宽资源被固定), 这会导致数据量增加从而使得带宽资源被过度占用.
Candidates include furthermore, Redis authors believe that clusters typically do not exceed 1000 instances.
候选者 :那么系统就采用了16384个样本作为基数;即则能够有效地将数据分布到Redis集群的不同实例上;从而避免了在数据交换过程中出现过高的带宽消耗。
[

该页面上展示的图像
面试官 :了解了
面试官询问道:「你了解这个原因吗?」对数据进行分区在Redis中的确采用了「哈希槽」这一种模式而非采用一致性的哈希算法
在当前分析中,在我的理解下
候选方案:相比于传统的固定取模方法,“一致性哈希算法”的优势在于,在系统节点群组进行新增或删除操作时,“一致性哈希算法”只会导致少量数据受到影响。
候选者 :如果在集群中新增或者移除实例,在一致性哈希算法中,则必须明确哪些数据会被影响,并对这些受影响的数据执行数据迁移操作。
面试官 :嗯…
候选方案:而哈希冲突解决方式中,在集群中的每个实例均能获取其对应的槽位信息。
参与者:当客户端在提交请求前对key进行计算哈希值后,在该实例未获取到与之匹配的数据时,请问是否正确?
候选者:在集群的扩容或缩容过程中,均以「哈希槽」作为基本单位进行操作;总体而言而言,则是「实施」更为简便(简洁,高效且具有弹性).具体来说,在扩容或缩容过程中,在将部分槽重新分配后转移这些槽中的数据即可,并不会影响到集群中某个实例的所有数据.
[

面试官 :那你了解「服务端 路由」的大致原理吗?
候选者 :通常指的是有一个代理层负责接收并转发所有来自客户端的请求,并最终发送至Red Hat集群进行处理。
候选者 :上次最后面试的时候,也提到了,现在比较流行的是Codis
候选者 :其主要区别在于 Redis Cluster它是通过直接连接单个Redis实例实现的系统性架构设计;而Codis则采用了客户端连接Proxy的方式,并且通过Proxy将请求转发至多个不同的Redis实例进行处理以提高负载能力
[

方案:Codis对Key路由的处理方式与Redis Cluster采用了类似的架构设计。通过初始化1024个哈希表槽,并将其分派到各个Redis服务器中完成负载均衡的任务。
候选方案列表:哈希槽与Redis实例的数据对应关系由Zookeeper负责存储与管理;Proxy会实时获取最新数据对应关系,并本地缓存副本
面试官 :那如果我要扩容Codis Redis实例的流程是怎么样的?
候选人:即将新创建的Redis实例接入集群网络中,并随后将部分数据复制至新实例上。
整个流程大致如下:首先,在每个候选者内部实现一个Solt的迁移功能。具体来说,在每一个候选者中都会执行以下步骤:1. 在当前实例中提取并保存「原实例」的某些关键数据;2. 将这些关键数据通过网络传输至「目标实例」;3. 目标实例在接收到这些数据后会将确认信息反馈至原实例;4. 原实例在接收到确认信息后会删除之前向目标实例传输的数据;5. 以上步骤依次重复执行直至整个Solt的迁移过程完成。
候选方案:Codis同样采用了异步迁移机制。针对上述步骤中的「原实例」发送数据后无需等待「目标实例」返回ack响应便立即处理客户端的新请求。
候选者 会被标记为「readonly」状态,并且这种标记方式不会破坏数据的一致性。如果涉及迁移中的数据包含「write operation」(即修改操作),那么这些操作将导致客户端进行‘重试’处理,并最终将修改结果存储在‘目标实例’中。
[

除此之外,在处理bigkey字段时采取了异步迁移的方式进行数据传输操作。举个例子来说,在一个集合元素中有1万个数据项时,则会将这些数据项逐一发送到目标实例中(而不是直接一次性传输整个bigkey字段,因为大对象容易导致阻塞)。
面试官 :了解了。
[

该页面的图片路径为/large/008i3skNgy1gtas2d8m9ej32240u0483.jpg的target字段。
本文总结 :
分片集群存在的动机 :存在性能瓶颈问题,在高并发场景下会出现限制;也无法无限扩展资源(成本效益不高)。
分片集群 :需要解决「数据路由」和「数据迁移」的问题
Redis Cluster数据路由 :
- Redis Cluster预设每个集群拥有16384个哈希槽,并将这些哈希槽平均分配给集群中的各个实例。
- 集群中的各个实例会相互协调工作,在管理各自负责的哈希槽信息上达成一致(最终确保每个实例都能完整地映射所有相关数据)。
- 当客户端发起请求时,系统将通过CRC16算法计算出相应的Hash值,并对这些值进行模运算处理(即取余操作),从而确定具体的哈希槽位置以及对应的Redis实例存储空间。
使用16384个哈希槽的原因是什么?它既能确保Redis实例间数据分配相对均匀分布,又能避免由于交互槽信息交换而导致的严重网络性能开销问题。
Redis Cluster 为何选择采用哈希槽机制而非一致性分布式哈希算法?因为采用哈希槽机制能够实现简洁高效的设计,在面对业务扩展或收缩时只需调整对应的槽的数据量即可,并不会影响到整个Redis集群的整体运行状态。
Codis数据路由 :默认配置为1024个哈希槽。相关信息将被存储至Zookeeper集群。Proxy将本地化一份数据。当Redis集群实例发生变更时:Dashboard将同步更新Zookeeper节点及相关Proxy的信息。
Redis Cluster和Codis的数据迁移:该集群可实现同步数据转移;该系统可实现同步及异步数据转移
将新的Redis实例纳入到系统的集群中,并随后将部分数据转移至该新实例上(处于在线状态)。
