
搭建集群工作需要以下三个步骤:
1)准备节点。
2)节点握手。
3)分配槽。
Redis集群一般由多个节点组成,节点数量至少为6个才能保证组成完整高可用的集群。每个节点需要开启配置cluster-enabled yes,让Redis运行在集群模式下。建议为集群内所有节点统一目录,一般划分三个目录:conf、data、log,分别存放配置、数据和日志相关文件。把6个节点配置统一放在conf目录下,集群相关配置如下:
其他配置和单机模式一致即可,配置文件命名规则redis-{port}conf,准备好配置后启动所有节点。
Cluster集群启动过程如下图:
每个节点目前只能识别出自己的节点信息,可以执行cluster nodes命令获取集群节点状
态。
节点握手是指一批运行在集群模式下的节点通过Gossip协议彼此通信,达到感知对方的过程。节点握手是集群彼此通信的第一步,由客户端发起命令:cluster meet{ip}{port}
cluster meet命令是一个异步命令,执行之后立刻返回。内部发起与目标节点进行握手通信,握手通信过程:
1)节点6379本地创建6380节点信息对象,并发送meet消息。
2)节点6380接受到meet消息后,保存6379节点信息并回复pong消息。
3)之后节点6379和6380彼此定期通过ping/pong消息进行正常的节点通
信。
分别执行meet命令让其他节点加入到集群中,
最后执行cluster nodes命令确认6个节点都彼此感知并组成集群。
节点建立握手之后集群还不能正常工作,这时集群处于下线状态,所有的数据读写都被禁止,通过cluster info命令可以获取集群当前状态。
Redis集群把所有的数据映射到16384个槽中。每个key会映射为一个固定的槽,只有当节点分配了槽,才能响应和这些槽关联的键命令。通过cluster addslots命令为节点分配槽。这里利用bash特性批量设置槽(slots),命令如下:
执行cluster info查看集群状态,如下所示:
当前集群状态是OK,集群进入在线状态。所有的槽都已经分配给节点,执行cluster nodes命令可以看到节点和槽的分配关系:
集群模式下,Reids节点角色分为主节点和从节点。首次启动的节点和被分配槽的节点都是主节点,从节点负责复制主节点槽信息和相关的数据。使用cluster replicate{nodeId}命令让一个节点成为从节点。其中命令执行必须在对应的从节点上执行,nodeId是要复制主节点的节点ID,命令如下:
Redis集群模式下的主从复制使用了之前介绍的Redis复制流程,依然支持全量和部分复制。复制(replication)完成后,整个集群的结构如图:
集群搭建需要很多步骤当集群节点众多时,必然会加大搭建集群的复杂度和运维成本。因此Redis官方提供了redis-tribrb工具方便我们快速搭建集群。
redis-tribrb是采用Ruby实现的Redis集群管理工具。内部通过Cluster相关命令帮我们简化集群创建、检查、槽迁移和均衡等常见运维 *** 作,使用之前需要安装Ruby依赖环境。
1、安装Ruby:
2、安装rubygem redis依赖:
3、安装redis-tribrb:
4、安装完Ruby环境后,执行redis-tribrb命令确认环境是否正确,输出如
下:
首先我们跟之前内容一样准备好节点配置并启动:
启动好6个节点之后,使用redis-tribrb create命令完成节点握手和槽分配过程,命令如下:
--replicas参数指定集群中每个主节点配备几个从节点,这里设置为1。
如果部署节点使用不同的IP地址,redis-tribrb会尽可能保证主从节点不分配在同一机器下,因此会重新排序节点列表顺序。节点列表顺序用于确定主从角色,先主节点之后是从节点。创建过程中首先会给出主从节点角色分配的计划,当我们同意这份计划之后输入yes,redis-tribrb开始执行节点握手和槽分配 *** 作。
集群完整性指所有的槽都分配到存活的主节点上,只要16384个槽中有一个没有分配给节点则表示集群不完整。可以使用redis-tribrb check命令检测之前创建的集群是否成功,check命令只需要给出集群中任意一个节点地址就可以完成整个集群的检查工作,命令如下:
Caused by: orgspringframeworkdataredisRedisConnectionFailureException: Could not get a resource from the pool; nested exception is redisclientsjedisexceptionsJedisConnectionException: Could not get a resource from the pool
at orgspringframeworkdataredisconnectionjedisJedisExceptionConverterconvert(JedisExceptionConverterjava:67)
at orgspringframeworkdataredisconnectionjedisJedisExceptionConverterconvert(JedisExceptionConverterjava:41)
at orgspringframeworkdataredisPassThroughExceptionTranslationStrategytranslate(PassThroughExceptionTranslationStrategyjava:37)
at orgspringframeworkdataredisconnectionjedisJedisClusterConnectionconvertJedisAccessException(JedisClusterConnectionjava:3999)
at orgspringframeworkdataredisconnectionjedisJedisClusterConnectionsetEx(JedisClusterConnectionjava:717)
at orgspringframeworkdatarediscoreDefaultValueOperations 11doInRedis(DefaultValueOperationsjava:186)
at orgspringframeworkdatarediscoreRedisTemplateexecute(RedisTemplatejava:207)
at orgspringframeworkdatarediscoreRedisTemplateexecute(RedisTemplatejava:169)
at orgspringframeworkdatarediscoreAbstractOperationsexecute(AbstractOperationsjava:91)
at orgspringframeworkdatarediscoreDefaultValueOperationsset(DefaultValueOperationsjava:182)
at iorenrencommonutilsSuppleDataUtilsprovideIcc(SuppleDataUtilsjava:107)
at iorenrenmodulesjobtaskTaskIcctaskFour(TaskIccjava:242)
10 common frames omitted
Caused by: redisclientsjedisexceptionsJedisConnectionException: Could not get a resource from the pool
at redisclientsutilPoolgetResource(Pooljava:53)
at redisclientsjedisJedisPoolgetResource(JedisPooljava:226)
at redisclientsjedisJedisSlotBasedConnectionHandlergetConnectionFromSlot(JedisSlotBasedConnectionHandlerjava:66)
at redisclientsjedisJedisClusterCommandrunWithRetries(JedisClusterCommandjava:116)
at redisclientsjedisJedisClusterCommandrunWithRetries(JedisClusterCommandjava:141)
at redisclientsjedisJedisClusterCommandrunWithRetries(JedisClusterCommandjava:141)
at redisclientsjedisJedisClusterCommandrunWithRetries(JedisClusterCommandjava:141)
at redisclientsjedisJedisClusterCommandrunWithRetries(JedisClusterCommandjava:141)
at redisclientsjedisJedisClusterCommandrunBinary(JedisClusterCommandjava:60)
at redisclientsjedisBinaryJedisClustersetex(BinaryJedisClusterjava:268)
at orgspringframeworkdataredisconnectionjedisJedisClusterConnectionsetEx(JedisClusterConnectionjava:715)
18 common frames omitted
Caused by: redisclientsjedisexceptionsJedisConnectionException: javanetSocketTimeoutException: connect timed out
at redisclientsjedisConnectionconnect(Connectionjava:207)
at redisclientsjedisBinaryClientconnect(BinaryClientjava:93)
at redisclientsjedisBinaryJedisconnect(BinaryJedisjava:1767)
at redisclientsjedisJedisFactorymakeObject(JedisFactoryjava:106)
at orgapachecommonspool2implGenericObjectPoolcreate(GenericObjectPooljava:868)
at orgapachecommonspool2implGenericObjectPoolborrowObject(GenericObjectPooljava:435)
at orgapachecommonspool2implGenericObjectPoolborrowObject(GenericObjectPooljava:363)
at redisclientsutilPoolgetResource(Pooljava:49)
28 common frames omitted
Caused by: javanetSocketTimeoutException: connect timed out
at javanetDualStackPlainSocketImplwaitForConnect(Native Method)
at javanetDualStackPlainSocketImplsocketConnect(DualStackPlainSocketImpljava:85)
at javanetAbstractPlainSocketImpldoConnect(AbstractPlainSocketImpljava:350)
at javanetAbstractPlainSocketImplconnectToAddress(AbstractPlainSocketImpljava:206)
at javanetAbstractPlainSocketImplconnect(AbstractPlainSocketImpljava:188)
at javanetPlainSocketImplconnect(PlainSocketImpljava:172)
at javanetSocksSocketImplconnect(SocksSocketImpljava:392)
at javanetSocketconnect(Socketjava:589)
at redisclientsjedisConnectionconnect(Connectionjava:184)
35 common frames omitted
最近在本地测试通过springboot基础redis的方式连接redis集群,启动的时候没有报错。
到时当执行保存,查询到redis的 *** 作的时候,报上面的错误。
首先看到错误信息,先是connect timed out后,再出现的Could not get a resource from the pool错误。一开始排查这个错误,由于使用的是默认连接池,debug的时候,也看到有装载连接池,为啥报错呢。本地通过redis-manger工具,连接集群地址也可以连接访问。从日志也看不出还有其他的错误信息。
于是自己写了main方法直接 *** 作JedisCluster的方式来测试,出现的错误跟上面全文一样。用抓包工具wireshark过滤集群的地址,查看连接的消息也是正常的。
另外一个项目使用的reddison客户端,于是测试了deamo用reddison来连接集群地址,启动的时候,发现报如下错误
乖乖,恍然大悟;102871731地址是服务器的内网地址,本机是连不上内网地址的,只有通过外网地址连接。而我配置的是外网地址。然而,客户端在集群同步和心跳检查的时候,是拉取的集群信息,redis集群信息里面的节点的信息
配置的是内网地址。客户端就通过这个内网地址来同步信息了。
再次同wireshark过滤集群102871731,发现有大量的超时重传的包,也没有响应。
1Redis集群是一个可以在多个Redis节点之间进行数据共享的设施(installation)
2Redis集群不支持那些需要同时处理多个键的Redis命令,因为执行这些命令需要在多个Redis节点之间移动数据,并且在高负载的情况下,这些命令将降低Redis集群的性能,并导致不可预测的行为。(使用ack协议)
3Redis集群通过分区(partition)来提供一定程度的可用性(availability):即使集群中有一部分节点失效或者无法进行通讯,集群也可以继续处理命令请求。
4Redis集群有将数据自动切分(split)到多个节点的能力。
#高性能:
1在多分片节点中,将16384个槽位,均匀分布到多个分片节点中
2存数据时,将key做crc16(key),然后和16384进行取模,得出槽位值(0-16384之间)
3根据计算得出的槽位值,找到相对应的分片节点的主节点,存储到相应槽位上
4如果客户端当时连接的节点不是将来要存储的分片节点,分片集群会将客户端连接切换至真正存储节点进行数据存储
5客户端与redis节点直连,不需要中间proxy层客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可
6Redis Cluster解决了redis资源利用率的问题
#高可用
7在搭建集群时,会为每一个分片的主节点,对应一个从节点,实现slaveof功能,同时当主节点down,实现类似于sentinel的自动failover的功能。
1在集群中,会把所有节点分为16384个槽位
2槽位的序号是 0 - 16383,序号不重要,数量才重要
3每一个槽位分配到数据的概率是一样
1在集群里面,节点会对其他节点进行下线检测。
2当一个主节点下线时,集群里面的其他主节点负责对下线主节点进行故障移。
3换句话说,集群的节点集成了下线检测和故障转移等类似 Sentinel 的功能。
相信各位在使用redis集群的时候,对于redis集群中的批量 *** 作都会有一个现象:使用redis集群进行批量获取数据的时候,效率总是不高,取一次数据要达到几百毫秒,当你 *** 作的数据是百万级别的时候,你就会发现redis的读取效率压根就不能接受。接下来告诉大家如何进行了解
### redis集群的哈希槽
redis集群中内置了16384个哈希槽,当一个key值准备存储的时候,是先通过将key进行 crc16 校验,校验后的值对16384取值,得到的值就是该key所在的槽(slot);redis集群中,节点上的槽是连续的一段,因此通过我们计算key得到的slot,就能判断该key是在存储在哪个节点上的。
#### 如何判断redis集群中各个节点上的slot分布?
使用命令 : cluster nodes 或者 cluster slots
#### 如何知道一个key值对应的槽
使用命令: cluster keyslots {key}
#### 提高效率的解决方案
因此,通过上面我们就可以知道key值存储对应的reids集群的节点,因此我们可以做以下处理:将你所需要的key按照槽的值进行分批,用单点连接的形式连接到某个redis节点上,批量取处于同一个节点上的key。
注意:
- 一定要用单点的形式进行连接,还是使用集群方式连接的话,就算是处于一个节点,效率也是没有提高的;
- redis集群单点连接的话,不能使用mget,因此mget只能取位于同一个 slot 上的,你可以使用pipeline进行事务处理;
### 一次具体的实现
目前我使用的语言的php,借鉴了
[crc16算法计算](>
1将需要 *** 作的key计算出对应的solt,得到hostAndPort,分组存放在一个map中。(Map<JedisPool, List<String>> node2keys = new HashMap<>())
2通过得到的JedisPool,分开使用jedis的pipeline执行
3将返回数据组装返回
1先得到一个JedisCluster
2实现key的分组
调用示例
Redis哨兵集群模式,每个节点都保存全量同步数据,冗余的数据比较多。从Redis30开始官方推出了RedisCluster集群模式 ,采用分片集群模式,可以减少冗余数据,但也提高了集群成本,RedisCluster集群最大的优点: 动态扩容、缩容 。
以最简单的为例,分别搭建3组节点,每组1主2从,如上图示。因机器有限,3组节点分别搭在3个虚拟机中,3台虚拟机IP: 19216810051、19216810052、19216810053 ,每组节点端口: 6381、6382、6383
配置文件基本和原来的一样,主要区别是要开启CLuster集群模式,
3台虚拟机,9个node节点分别启动
因为搭建在了 3台虚拟主机上,所以要先关闭防火墙
用命令将9台节点组成集群,在一台机器运行就行
连接集群和单机基本一样,但需要注意2点:
连接以后用 cluster nodes 命令查看节点信息,测试写入
杀死52上的主节点,等几秒钟,查看集群情况
然后重启,在查看集群情况,如下图:
集群减少一组主从节点,现在有3组,先把51:6381这一组去掉吧。
19216810051:6381 的从节点分别是 19216810052:6382 , 19216810053:6382 ,直接删除这2个,注意 *** 作时都用到node id
主节点 19216810051:6381 的slots转移到其他1个或多个节点,看硬件配置分配,这里就直接都给 19216810053:6381 吧。
删除之前,先查看一遍节点信息,确认槽位已经转移,再删除
集群扩容肯定是增加一组,即主从一块增加
启动好要增加的服务节点,新增主节点,命令中第2个地址是现在集群中任一个node节点的,注意不要搞错
从节点有2个,分别添加
Redis Cluster 集群分区方案采用去中心化的方式,包括:sharding(分区)、replication(复制)、failover(故障转移)
Redis Cluster 由多个Redis节点组构成,是一个P2P(point to point)无中心节点的集群架构,依靠Gossip协议传播集群
Gossip协议是一个通信协议,一种传播消息的方式。
起源于:病毒传播
Gossip协议基本思想:
一个节点周期性(每秒)随机选择一些节点,并把信息传递给这些节点。
这些收到信息的节点接下来会做同样的事情,即把这些信息传递给其他一些随机选择的节点。
信息会周期性的传递给N个目标节点。这个N被称为 fanout (扇出)
gossip协议包含多种消息,包括meet、ping、pong、fail、publish等等
通过gossip协议,cluster可以提供集群间状态同步更新、选举自助failover等重要的集群功能。
redis-cluster把所有的物理节点映射到[0-16383]个 slot 上,基本上采用平均分配和连续分配的方式。
比如上图中有5个节点,这样在 Redis Cluster 创建时,slot槽可按下表分配
cluster 负责维护节点和slot槽的对应关系 value------>slot-------->节点
当需要在 Redis 集群中放置一个 key-value 时,redis 先对 key 使用 crc16 算法算出一个结果,然后把
结果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,redis 会根据节点
数量大致均等的将哈希槽映射到不同的节点。
比如:
set name zhangsan
hash("name")采用crc16算法,得到值:1324203551%16384=15903
根据上表15903在13088-16383之间,所以name被存储在Redis5节点。
slot槽必须在节点上连续分配,如果出现不连续的情况,则RedisCluster不能工作。
redis版本说明
redis505
服务器说明
启动 7001、7002、7003、7011、7012、7013
配置启动脚本
三主三从
客户端连接集群
-c 以集群方式连接
扩容节点数据必须为空
启动 7004、7014
将 7004、7014 添加到集群
只能删除数据为空的节点
集群中的每个节点都会定期地(每秒)向集群中的其他节点发送PIN
如果在一定时间内(cluster-node-timeout),发送ping的节点A没有收到某节点B的pong回应,则A将B
标识为pfail。
A在后续发送ping时,会带上B的pfail信息, 通知给其他节点。
如果B被标记为pfail的个数大于集群主节点个数的一半(N/2 + 1)时,B会被标记为fail,A向整个集群
广播,该节点已经下线
其他节点收到广播,标记B为fail。
采用 raft 协议
每个从节点,都根据自己对master复制数据的offset,来设置一个选举时间,offset越大(复制数
据越多)的从节点,选举时间越靠前,优先进行选举。
slave 通过向其他master发送FAILVOER_AUTH_REQUEST 消息发起竞选,
master 收到后回复FAILOVER_AUTH_ACK 消息告知是否同意。
slave 发送FAILOVER_AUTH_REQUEST 前会将currentEpoch 自增,并将最新的Epoch 带入到
FAILOVER_AUTH_REQUEST 消息中,如果自己未投过票,则回复同意,否则回复拒绝。
所有的 Master 开始slave选举投票,给要进行选举的slave进行投票,如果大部分master node(N/2 +
1)都投票给了某个从节点,那么选举通过,那个从节点可以切换成master。
RedisCluster失效的判定 :
1、集群中半数以上的主节点都宕机(无法投票)
2、宕机的主节点的从节点也宕机了(slot槽分配不连续)
当slave 收到过半的master 同意时,会成为新的master。此时会以最新的Epoch 通过PONG 消息广播
自己成为master,让Cluster 的其他节点尽快的更新拓扑结构(nodeconf)。
自动切换
就是上面讲的从节点选举
手动切换
人工故障切换是预期的 *** 作,而非发生了真正的故障,目的是以一种安全的方式(数据无丢失)将当前
master节点和其中一个slave节点(执行cluster-failover的节点)交换角色
1、向从节点发送cluster failover 命令(slaveof no one)
2、从节点告知其主节点要进行手动切换(CLUSTERMSG_TYPE_MFSTART)
3、主节点会阻塞所有客户端命令的执行(10s)
4、从节点从主节点的ping包中获得主节点的复制偏移量
5、从节点复制达到偏移量,发起选举、统计选票、赢得选举、升级为主节点并更新配置
6、切换完成后,原主节点向所有客户端发送moved指令重定向到新的主节点
以上是在主节点在线情况下。
如果主节点下线了,则采用cluster failover force或cluster failover takeover 进行强制切换。
我们知道在一主一从的情况下,如果主从同时挂了,那整个集群就挂了。
为了避免这种情况我们可以做一主多从,但这样成本就增加了。
Redis提供了一种方法叫副本漂移,这种方法既能提高集群的可靠性又不用增加太多的从机。
Master1宕机,则Slaver11提升为新的Master1
集群检测到新的Master1是单点的(无从机)
集群从拥有最多的从机的节点组(Master3)中,选择节点名称字母顺序最小的从机(Slaver31)漂移
到单点的主从节点组(Master1)。
具体流程如下(以上图为例):
1、将Slaver31的从机记录从Master3中删除
2、将Slaver31的的主机改为Master1
3、在Master1中添加Slaver31为从节点
4、将Slaver31的复制源改为Master1
5、通过ping包将信息同步到集群的其他节点
以上就是关于Redis Cluster集群的搭建全部的内容,包括:Redis Cluster集群的搭建、jedis连接集群报Could not get a resource from the pool错误、Redis Cluster分布式集群等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)