当前位置: 首页 > 后端技术 > Java

为什么要使用Redis集群?

时间:2023-04-01 15:10:03 Java

采访者:说说Redis的shardingcluster,先说说RedisCluster吧?面试官:RedisCluser是Redis3.x才有的官方集群方案。你对它了解多少?候选人:嗯,我们为什么不从基础开始呢?考生:前面讲Redis的时候,提到的Redis是一个“单实例”来存储所有的数据。候选:1、主从模式实现读写分离架构,让多个从服务器承载“读流量”,但面对“写流量”时,始终只有主服务器在抵抗。考生:2、“纵向扩展”升级Redis服务器的硬件能力,但升级到一定程度并不划算。考生:纵向扩展意味着“内存大”,Redis持久化的“成本”会增加(Redis做RDB持久化,满满当当fork子进程可能会占用过多内存,导致主线程阻塞时间太长long)候选者:因此,“单实例”是一个有瓶颈的候选者:“纵向扩展”不行,只能“横向扩展”。考生:使用多个Redis实例组成一个集群,将数据按照一定的规则“分发”到不同的Redis实例中。当集群中所有Redis实例的数据加起来,那么这个数据就是整体的一个候选:其实就是“分布式”的概念(:不过在Redis中,好像更多的人叫“shardedclusters”?考生:从上一节了解到,要想“分布式存储”,就不能避免“分布式”数据(也就是路由的意思)考生:我们先从RedisCluster说起,它的“路由”是todo在客户端(SDK集成了路由转发功能)候选:RedisCluster的数据分发逻辑涉及到“HashSolt”的概念候选:RedisCluster默认有16384个哈希槽,这些哈希槽会被分配给不同的Redis实例Candidates:至于怎么“分”,可以直接平分,也可以“手动”设置每个Redis实例的hash槽,都由我们自己决定Candidate:重要的是16384我们要全部瓜分,不能有剩菜!考生:客户端有数据要写入时,会先根据CRC16算法计算出key的16位值(你可以理解为做hash),然后将得到的值对16384取模考生:取后取模,自然得到其中一个hash槽,然后就可以把数据插入到分配给hash槽的Redis实例中面试官:那么问题来了。既然客户端已经通过哈希算法计算出了哈希槽的位置,那么客户端如何知道哈希槽在哪个Redis实例上呢?考生:是的,在集群中每个Redis实例都会“传播”到其他实例它负责哪些hashslots。这样每个Redis实例就可以记录下“所有hash槽和实例”的关系(:候选人:是建立了这个映射关系后,client也会“缓存”一份到自己的本地,所以client自然会知道在哪个Redis实例上操作面试官:那我还有一个问题,我也可以新建一个Redis实例怎么增加或者删除Redis实例?考生:集群在删除或者增加一个Redis实例的时候,总会有变化一个Redis实例的哈希槽关系Candidate:变化的信息会通过消息发送到整个集群,所有的Redis实例都会知道这个变化,然后更新自己保存的映射candidate:但此时,client其实是不知道的(:考生:所以,当客户端请求选择了某个Key时,仍然会向“原来”的Redis实例发起请求。原来的Redis实例会返回“moved”命令,告诉客户端去新的Redis实例去请求。“cachehashslots和instances的映射关系”候选:总结一下:数据迁移完成后,客户端会收到“moved”命令,更新本地缓存。采访者:数据还没有完成迁移完成了吗?考生:如果数据还没有完全迁移,那么此时会返回客户端“询问”命令。它还允许客户端请求一个新的Redis实例,但是此时客户端不会更新本地缓存。面试官:明白面试官:说白了,如果集群Redis实例有变化,因为面试的时候Redis实例会“通信”客户端请求的数据已打开。面试官:如果已经迁移完成,则返回“move”命令,告诉客户端到哪个Redis实例中查找Want数据,客户端应该更新自己的缓存(映射关系)面试官:如果正在迁移,则返回“ack”命令告诉客户端应该去哪个Redis实例找数据考生:是你...面试官:那你知道为什么有16384个哈希槽吗?候选人:嗯,这个。是这样的,Redis实例之间的“通信”会互相交换“槽信息”,那么如果槽太多(意味着网络包会变大),网络包会变大,是不是意味着会被“过度占用”网络带宽的候选者:还有一个是Redis的作者认为集群一般不会超过1000个实例。它不会在交换数据时导致过多的带宽使用。面试官:知道了面试官:那你知道Redis为什么要用“hashslot”的方式对数据进行分区吗?Notacandidateforaconsistenthashalgorithm:在我的理解中,一致的哈希算法有一个“哈希环”。当客户端请求时,它会对Key进行散列以确定其在散列环上的位置。然后顺时针回头寻找第一个真正的候选节点:一致性哈希算法相对于“传统固定模”的优势在于,如果集群中需要添加或删除一个实例,只会影响一小部分数据候选:但是如果在集群中增加或删除实例,在一致性哈希算法下,你需要知道“哪部分数据”受到了影响,你需要迁移受影响的数据。面试官:是的...考生:关于hashslots的方式,通过上面我们已经可以知道:集群中的每个实例都可以获取到slot的相关信息考生:client对key进行hash操作后,如果请求的实例没有相关数据,实例会返回“redirect”命令告诉客户端去哪里请求候选:集群的扩容和缩容都是以“hashslot”为基础操作单元。一般来说,“实施”会更简单(简洁、高效、灵活)。过程大概是重新分配一些slot,然后迁移slot中的数据,不影响集群中一个实例的所有数据。面试官:你了解“服务器端路由”的一般原理吗?考生:嗯,服务端路由一般是指有一个代理层,专门连接客户端的请求,然后转发到Redis集群进行处理。应聘者:我上次面试的时候也提到了。Codiscandidate:它和RedisCluster最大的不同在于,RedisCluster直接连接Redis实例,而Codisclients直接连接Proxy,再由Proxy分发到不同的Redis实例进行处理。候选人:Codis中的Key路由方案与RedisCluster非常相似。Codis初始化了1024个哈希槽,然后将它们分发到不同的Redis服务器上。考生:hash槽和Redis实例的映射关系由Zookeeper存储和管理,Proxy会通过CodisDashBoard获取最新的映射关系并缓存到本地面试官:如果想扩容CodisRedis实例,流程是怎样的?考生:简单来说:在集群中添加一个新的Redis实例,然后将一些数据迁移到新实例“目标实例”中。2、“目标实例”接收到数据后,返回一个ack给“原实例”。3、“原实例”收到ack后,在本地删除刚刚发送给“目标实例”的数据。4、不断循环1、2、3步,直到整个solt迁移完成,继续接收client请求。考生:将未迁移的数据标记为“只读”,不会影响数据的一致性。如果对正在迁移的数据有“写操作”,就会让客户端“重试”,最后写入“目标实例”,比如有10000个set元素,“原实例”可能向“目标实例”发送10000条命令,而不是一次迁移整个bigkey(因为大对象容易造成阻塞)面试官:明白了。本文总结:分片集群诞生原因:高并发下写性能会遇到瓶颈&&不能垂直无限扩展(不划算)分片集群:需要解决“数据路由”和“数据迁移”的问题》RedisCluster数据路由:RedisCluster在一个集群中默认有16384个哈希槽,哈希槽会分配给Redis集群中的实例。有完整的映射关系)客户端请求时,使用CRC16算法计算Hash值取模16384,自然得到hash槽进而得到对应的Redis实例位置为什么16384hash槽:16384可以使分配给Redis实例的数据比较统一,不会影响Redis实例之间的交互槽信息,造成严重的网络性能开销。为什么RedisCluster使用哈希槽而不是一致性哈希算法:哈希槽的实现相对简单高效,每次扩缩容只需要移动对应的Solt(slot)数据,一般不会移动整个Redis实例Codis数据路由:默认分配1024个hashslot,映射相关信息会保存到Zookeeper集群.Proxy会在本地缓存一个副本。当Redis集群实例发生变化时,DashBoard会更新Zookeeper和Proxy的映射信息。RedisCluster和Codis数据迁移:RedisCluster支持同步迁移,Codis支持同步迁移&&异步迁移向集群添加新的Redis实例,然后将部分数据迁移到新的实例(线上)欢迎关注我的微信公众号【Java3y】聊聊Java面试,在线面试官系列持续更新中!【在线面试官-手机版】系列,每周两篇,持续更新中!【在线面试官-电脑】系列每周两篇持续更新中!原创不易!!一连求三!!