引言在redis cluster出现之前,通常通过以下几种方式搭建redis集群:
认识Redis Cluster Redis Cluster是由多个同时服务于一个数据集合的Redis实例组成的整体,对于用户来说,用户只关注这个数据集合,而整个数据集合的某个数据子集存储在哪个节点对于用户来说是透明的。Redis Cluster具有分布式系统的特点,也具有分布式系统如何实现高可用性与数据一致性的难点 Redis Cluster特点如下:
Redis Cluster搭建集群中至少应该有奇数个节点,所以至少有三个节点,每个节点至少有一个备份节点,所以下面使用6节点(主节点、备份节点由redis-cluster集群确定)。 2.将redis.conf 配置文件复制6份,分别修改对应的端口,6个文件如下:redis_7001.conf,redis_7002.conf,redis_7003.conf,redis_7004.conf,redis_7006.conf 3.安装redis-trib所需的 ruby脚本 yum install ruby 4.启动redis服务 ./redis-server ../redis_7001.conf 5.创建redis-cluster redis-trib.rb create --replicas 1 127.0.0.1:6310 127.0.0.1:6320 127.0.0.1:6330 127.0.0.1:6340 127.0.0.1:6350 127.0.0.1:6360 使用create命令 --replicas 1 参数表示为每个主节点创建一个从节点,其他参数是实例的地址集合。 6.redis集群的测试 ./redis-cli -c -p 7001 根据redis-cluster的key值分配,name应该分配到节点7002[5461-10922]上,上面显示redis cluster自动从7001跳转到了7002节点。 Redis Cluster实现原理Redis Cluster 的原理其实也很简单,用一张大图概括如下:
1. 槽(slot)概念redis cluster引入槽的概念,一定要与一致性hash的槽区分!这里每一个槽映射一个数据集。 CRC16(key) & 16384 这里计算结果发送给redis cluster任意一个redis节点,这个redis节点发现他是属于自己管辖范围的,那就将它放进去;不属于他的槽范围的话,由于redis之间是相互通信的,这个节点是知道其他redis节点的槽的信息,那么会告诉他去那个redis节点去看看,这样就实现了服务端对于槽、节点、数据的管理。 当redis集群需要扩容的时候,由于每个节点维护的槽的范围是固定的,当有新加入的节点时,是不会干扰到其他节点的槽的,必须是以前的节点将使用槽的权利分配给你,并且将数据分配给你,这样,新的节点才会真正拥有这些槽和数据。这种实现还处于半自动状态,需要人工介入。主要的思想是:槽到集群节点的映射关系要改变,不变的是键到槽的映射关系。 Redis集群要保证16384个槽对应的node都正常工作,如果某个node发生故障,那它负责的slots也就失效,整个集群将不能工作。为了增加集群的可访问性,官方推荐的方案是将node配置成主从结构,即一个master主节点,挂n个slave从节点。这时,如果主节点失效,Redis Cluster会根据选举算法从slave节点中选择一个上升为主节点,整个集群继续对外提供服务。 2. 位序列结构某个Master是怎么知道某个槽自己是不是拥有呢?Master节点维护着一个16384/8字节的位序列,Master节点用bit来标识对于某个槽自己是否拥有。比如对于编号为1的槽,Master只要判断序列的第二位(索引从0开始)是不是为1即可。 如上面的序列,表示当前Master拥有编号为1,101,102的槽。集群同时还维护着槽到集群节点的映射,是由长度为16384类型为节点的数组实现的,槽编号为数组的下标,数组内容为集群节点,这样就可以很快地通过槽编号找到负责这个槽的节点。位序列这个结构很精巧,即不浪费存储空间,操作起来又很便捷。 redis节点之间如何通信的?
3. slot 数据迁移这里6382为新加入的节点,一开始是没有槽的,所以进行slot的迁移。 迁移数据的流程图: MIGRATING状态预备迁移槽的时候槽的状态首先会变为MIGRATING状态,这种状态的槽会实际产生什么影响呢?当客户端请求的某个Key所属的槽处于MIGRATING状态的时候,影响有下面几条:
IMPORTING状态槽从MasterA节点迁移到MasterB节点的时候,槽的状态会首先变为IMPORTING。IMPORTING状态的槽对客户端的行为有下面一些影响: 正常命令会被MOVED重定向,如果是ASKING命令则命令会被执行,从而Key没有在老的节点已经被迁移到新的节点的情况可以被顺利处理;如果Key不存在则新建;没有ASKING的请求和正常请求一样被MOVED,这保证客户端node映射关系出错的情况下不会发生写错; 4. 客户端路由moved重定向其中,槽直接命中的话,就直接返回槽编号: 槽不命中,返回带提示信息的异常,客户端需要重新发送一条命令: ask重定向在扩容缩容的时候,由于需要遍历这个节点上的所有的key然后进行迁移,是比较慢的,对客户端是一个挑战。因为假设一个场景,客户端访问某个key,节点告诉客户端这个key在源节点,当我们再去源节点访问的时候,却发现key已经迁移到目标节点. moved重定向和ask重定向对比
本公众号团队成员由饿了么、阿里、蚂蚁金服等同事组成,关注架构师之巅,可以了解最前沿的技术。 |
|