在单台服务器的情况下,随着用户量的增加和数据量的增大,系统的响应时间和处理能力可能会受到限制,影响系统的性能。为了解决这个问题,可以使用集群技术将多台服务器组合在一起,共同处理用户的请求和数据,从而提高系统的性能
集群技术的核心思想是将任务分配到不同的服务器上,让每台服务器都参与到任务的处理中来。通过有效的负载均衡机制和任务调度算法,集群可以实现高效的任务分配和处理,从而提高系统的性能和吞吐量
在单台服务器的情况下,如果服务器发生故障或者停机,那么整个系统将会停止工作,用户的请求和数据也可能会丢失。为了避免这种情况,可以使用集群技术将多台服务器组合在一起,提高系统的可靠性和容错能力
集群技术可以通过多台服务器之间的数据备份和故障转移来保障系统的可靠性。如果其中一台服务器发生故障或者停机,其他的服务器可以接替它的工作,从而确保系统的持续运行和数据安全
随着计算机应用领域的不断扩大,对于计算机系统的性能和可靠性要求也越来越高。在这种背景下,集群成为提高系统性能和可靠性的重要方式之一
在集群中,通常会有一台主服务器负责任务的分配和调度,其他服务器作为从服务器负责任务的执行和处理。主服务器可以使用负载均衡机制和任务调度算法来均衡各个服务器之间的负载,从而提高集群的性能和可靠性。此外,集群还可以使用数据备份和故障转移机制来提高系统的可靠性和容错能力。如果其中一台服务器出现故障,其他服务器可以接替它的工作,确保系统的持续运行和数据安全
在集群中,服务器之间的通信通常使用网络协议进行,比如TCP/IP协议、HTTP协议等。集群中的服务器可以在同一地理位置,也可以分布在不同的地理位置,它们之间可以通过Internet或专用网络进行通信
集群按照架构分类:主从集群、负载均衡器集群、分布式计算集群、高可用集群、容器集群、大数据集群、分布式存储集群
主从集群:主从集群通常使用数据库的复制机制来实现主从之间的数据同步,比如MySQL、Redis的主从复制技术,一般情况下主节点负责写,从节点负责读,主从节点数据是需要同步的
容量限制:单机Redis的容量是有限的,一旦数据量达到单机容量极限,就需要将数据存储到多台机器上进行扩容
高可用性:单机Redis存在单点故障的问题,如果单机Redis宕机,就会导致整个系统的服务中断,因此需要使用集群来实现高可用性和容错能力
并发性能:单机Redis的并发性能是有限的,一旦并发访问量达到一定程度,就会出现性能瓶颈和性能下降的问题,因此需要使用集群来提高并发性能
数据自动分片:Redis集群将数据自动分片存储到多台机器上,实现了数据的横向扩展,可以支持更大规模的数据存储
数据自动复制:Redis集群通过数据复制机制来保证数据的高可用性,即将主节点上的数据自动复制到多个从节点上,实现数据的备份和容错
自动故障转移:Redis集群可以自动进行故障转移,即在主节点宕机时,自动选举一个从节点作为新的主节点,保证系统的持续运行和服务的可用性
增强并发性能:Redis集群通过分片机制来增强并发性能,即将数据分散到多个节点上进行处理,从而避免单机性能的瓶颈问题
主从复制模式是三种模式中最简单的一种,多台Redis实例,以一台实例作为主机,其余的实例作为备份机,主机和从机的数据完全一致,主从复制提高了Redis读请求的吞吐量
主机支持数据的读和写操作,而从机只支持读和数据同步,客户端将数据写入主机后,由主机自动的将数据写入到从机中
在Redis中主从复制的同步策略有两种
注意
客户端写入数据到主机后同步流程
Redis增量复制的具体实现是基于主从节点之间的心跳机制和复制缓冲区的数据大小来确定的。当从节点接收到主节点的心跳消息时,会向主节点发送REPLCONF ACK 命令,其中offset表示从节点上次接收到主节点发送的复制偏移量。主节点在接收到ACK命令后,会检查从节点的复制偏移量,如果从节点复制偏移量小于主节点当前复制偏移量,则会将主节点的复制缓冲区中的数据发送给从节点,完成增量复制
Redis增量复制的请求频率不是固定的,而是根据主从节点之间的数据同步情况而定。如果从节点上次接收到主节点发送的复制偏移量比较旧,或者从节点的复制缓冲区中的数据比较少,那么从节点会更频繁地向主节点发送心跳消息,并请求增量复制。如果从节点的复制缓冲区中的数据比较多,或者从节点与主节点之间的网络连接较慢或不稳定,那么从节点可能会减少向主节点发送心跳消息的频率,以避免网络带宽的占用和数据同步的延迟
一般情况下,Redis主从节点之间的增量复制请求频率较高,可以实现较实时的数据同步。同时,Redis增量复制的性能和效率也取决于主从节点之间的网络连接质量、硬件配置和数据量等因素
主从复制模式当主节点宕机以后,需要手动将一台从节点切换到主节点,这个时候就需要人工干预,不仅麻烦还会造成一段时间内服务不可用,这时候就需要哨兵模式
Redis哨兵模式是在Redis 2.8版本中引入的。在此版本之前,Redis只提供了主从复制来实现高可用性和数据备份。但是,主从复制存在单点故障的问题,当主节点宕机时整个系统将无法提供服务。因此,Redis引入了哨兵模式来解决这个问题,使得Redis服务具备更高的可用性和数据一致性
哨兵模式的核心还是主从模式,只不过在主从模式的基础上增加了一个竞选机制,如果主节点宕机了,那么从所有的从节点中选出新的主节点。竞选机制的实现依赖于在系统中启动一个sentinel进程
sentinel会监控所有节点是否正常运行,通过发出命令返回监控节点的状态,在多sentinel模式下,sentinel之间也会互相监控
故障切换:当哨兵监测到master宕机,会自动将slave切换成master,然后通过发布订阅模式通知其他的从服务器,修改配置文件,让它们切换master。同时那台有问题的旧主也会变为新主的从,也就是说当旧的主即使恢复时,并不会恢复原来的主身份,而是作为新主的一个从
ping:哨兵节点启动后所有哨兵每隔1S会互相发送ping心跳,来检测其他哨兵节点是否正常
info:哨兵节点启动后每隔10S会向所有的主节点发送info指令,得到runid、role、各个slave的详细信息。再根据slave信息发送info指令获取slave的详细信息,会得到runid、role、master_host、master_port、offset...
pub/sub:每个哨兵节点每隔2S会在一个指定频道发布当前节点保存的主节点信息,其他哨兵节点会订阅该频道获取信息
哨兵监控master
某个哨兵发现主节点出故障后,会将当前主节点标记为+sdown,也就是标记为主观下线,单个哨兵认为有问题可能是网络抖动问题
后续如果其他哨兵节点也发现此主节点出现问题,当发现问题的哨兵数量大于quorym时,那么哨兵集群会认为此主节点出现故障,标记为odown状态,此时主节点被标记为客观下线
哨兵leader选举:当主节点被标记为客观下线时,就要在哨兵集群中选出一个哨兵节点来完成后面的故障转移工作,每个哨兵节点都可以成为leader,选举流程如下
故障转移实现流程:哨兵在监控阶段时就保存了主从节点信息,选举出leader哨兵后,哨兵要开始进行主从节点故障转移,从众多从节点中选举出一个主节点。主节点选举流程如下
更新状态:当故障转移后,需要更新当前的主从状态,流程如下
哨兵进程会使用PING命令的方式来检测各个主库和从库的网络连接情况,用来判断实例状态
如果哨兵发现主库或者从库响应超时(down-after-millisecond),那么哨兵会判定其为"主观下线"
如果该节点是主节点,那么哨兵会进一步判断是否需要对其进行故障切换,这时候会发送命令(SENTINEL is-master-down-by-addr)询问其他哨兵节点是否认为该主节点是主观下线,当达到指定数量(quorum)时,哨兵就会认为此主节点是客观下线
如果没有足够数量的哨兵同意主节点进入主观下线,主节点的主观下线状态就会被消除,若主节点重新向哨兵的PING命令返回有效回复,主节点的主观下线状态也会被消除
哨兵误判:主库本身没有故障,但由于哨兵的误判,判断它为下线状态。一旦启动主从切换,后续的选举和通知操作都会带来额外的计算和通信开销。因此,为了不必要开销,我们要严格注意误判的情况。在哨兵集群中,判定主库是否处于下线状态,不是由一个哨兵来决定的,而是只有大多数哨兵认为主库已经"主观下线",主库才会标记为"客观下线"。这种判断机制为:少数服从多数。同时会触发故障转移
关键属性
down-after-milliseconds:Redis Sentinel 配置文件中的一个属性,表示 Sentinel 发送 Ping 命令给实例之后,如果规定的时间内没有收到响应,就认为实例已经下线了,然后 Sentinel 就会开始判断实例的状态。这个属性的默认值为 30,000 毫秒 (30 秒)。可以通过修改配置文件的方式来更改该属性的值 SENTINEL is-master-down-by-addr:Redis Sentinel 提供的命令之一,用于向其它 Sentinel 节点询问某个主服务器是否已经下线。当一个 Sentinel 节点判定某个主服务器为主观下线后,会向其它 Sentinel 节点发送 SENTINEL is-master-down-by-addr 命令来确认这个主服务器是否已经客观下线,从而决定是否执行自动故障转移。这个命令需要提供主服务器的 IP 地址和端口号作为参数,响应内容则是一个字符串,表示主服务器的状态信息
quorum:哨兵模式中用来决定主节点是否被判定为客观下线的数量阈值。当有N个哨兵节点监控同一个主节点时,要求有至少N/2+1个哨兵节点判定该主节点为主观下线,此时才会被判定为客观下线,从而触发故障转移。这个数量阈值可以通过配置文件中的参数quorum来设置 SLAVEOF NO ONE:此命令是Redis用于将一个从节点转化为主节点的命令。执行该命令后,从节点会断开与其原来的主节点的连接,并开始独立工作,即成为一个新的独立的主节点 slaveof:此命令是Redis中用于配置主从复制关系的命令。它用于将一个Redis实例设置为另一个Redis实例的从服务器。这个命令在Redis的主从复制过程中是非常重要的,它让从服务器能够连接到主服务器,并且通过复制主服务器上的数据来保持与主服务器的数据一致性
高可用性:哨兵模式可以自动检测节点的可用性,并实现主从节点的自动切换,从而提高了 Redis 的高可用性、健壮性
灵活性:哨兵模式支持动态的添加、删除节点,可以根据需要扩展 Redis 的读写性能
无需人工干预:哨兵模式不需要人工干预,可以自动实现故障转移和节点恢复
延迟:哨兵模式需要检测节点的状态,判断主节点是否宕机,再进行故障转移,因此会增加一定的延迟
配置复杂:哨兵模式需要配置多个哨兵节点,需要合理设置节点的数量和 quorum 值,对于不了解 Redis 的开发人员来说,配置较为复杂
在线扩容复杂:Redis比较难支持在线扩容
所有主从节点保存的都是全量数据,浪费内存空间,没有实现真正的分布式存储,数据量过大时,主从同步严重影响master性能
故障转移时,在主节点选举结束之前,谁也不知道主从节点是谁,此时Redis会开启切换保护机制,禁止写操作,直到选举出新的主节点
高可用:多个主节点,每个主节点有对应多个从节点,主节点宕机RedisCluster机制会自动将某个从节点切换到主节点
扩展性
分布式存储:RedisCluster采用分片技术将数据均匀分布到多个节点上,每个节点只保存部分数据,避免了单个节点存储数据过大的问题,提高了存储容量和性能
自动数据迁移:RedisCluster支持自动数据迁移,当新增或删除节点时,会自动将数据迁移到其他节点上,保证数据均衡和数据完整性
redis在3.0上加入了 Cluster 集群模式,实现了 Redis 的分布式存储,也就是说每台 Redis 节点上存储不同的数据。cluster模式为了解决Redis单节点容量有限的问题,将数据按一定的规则分配到多台机器,内存/QPS不受限于单机,可受益于分布式集群高扩展性
RedisCluster是一种服务器Sharding技术,分片和路由都是在服务端实现,采用多主多从,每一个分区都是由一个Redis主节点和多个从节点组成,片区和片区之间是相互平行的。RedisCluster集群采用了P2P的网络拓扑架构,没有中心节点,所有节点通过Gossip协议通信,所有节点即存储数据也是控制节点
RedisCluster建议至少部署3个主节点和3个从节点,以保证数据的高可用性和负载均衡
总结:RedisCluster是可以达到主从复制架构、读写分离、哨兵集群、高可用效果的集群架构
原理:对Key进行Hash运算,然后将值对主节点数量取模,最后得到的数值就是此数据应存储到的主节点
优点:简单、快速、高效、适用于大规模快速查询
缺点:
数据分布不均匀:如果使用简单的Hash寻址算法,可能会出现数据倾斜的情况,导致某些节点负载过高,而某些节点负载过轻
数据迁移困难:使用Hash寻址算法进行数据分布后,如果需要添加或删除节点,就需要重新计算每个数据对应的节点,然后将其迁移到新节点上,这个过程非常繁琐,而且需要暂停对集群的写操作
扩展性受限:使用Hash寻址算法进行数据分布时,如果需要增加节点,那么需要将所有的数据重新计算分配,这样就会限制集群的扩展性
大量缓存重建问题:主节点如果宕机,那么Hash运算时根据现有存活节点进行取模,得到的数值与原有存储数据时的数值不匹配,请求走不到原有路由节点上,从而导致大量的key瞬间全部失效
原理:将所有的主节点进行Hash运算,再将Hash值映射到一个固定的Hash环上面。然后对Key进行Hash运算,将此Hash值与圆环上的各个点进行对比,将Hash值落在圆环上后进行顺时针旋转去寻找距离自己最近的一个节点,数据的存储与读取都在此节点进行
优点:保证任何一个主节点宕机,只会影响在之前那个主节点上的数据,此前的主节点宕机,查询时这部分数据会丢失,写入时沿着顺时针去到下一个主节点
缺点
数据丢失过大:在节点比较少的情况下, 丢失的数据量还是非常庞大的
缓存热点问题:如果某些数据被频繁地访问,会导致热点数据集中在某个节点上,造成负载不均
数据倾斜问题:节点数量较少时,由于数据分布不均,可能会导致某个节点负载过重,影响系统的性能。这是因为Hash函数的输出值在Hash环上并不是均匀分布的,而是有规律的。一致性哈希算法通过引入虚拟节点来解决这个问题,将每个物理节点映射到多个虚拟节点上,使得数据更加均匀地分布在环上
一致性问题:由于节点的添加或删除会影响哈希值的计算,可能会导致数据分布不均,这个问题可以通过一些技术手段来解决,例如虚拟节点、数据复制等
Redis使用此算法的原因
原理
16384槽位由来
CRC16算法产生的Hash值有16bit,该算法可以产生65536个值。值是分布在0~65535之间,那么其实在做取模运算时,我们是可以取模65536的,但是Redsi作者采取了16384
作者的回答是:Redis集群节点数量如果超过1000个那么会造成网络拥堵,所以建议节点数量不超过1000个,那么1000个节点使用16384个槽位完全够用了。如果使用65536个槽位会导致主节点之间交互心跳包时,浪费带宽。槽位数量过少不够用,过多浪费带宽,所以作者通过实测计算得出一个16384的值,不多不少刚刚好
4.1.Gossip通信协议概述
HashSlot算法解决的是数据读写问题,那么当集群节点数量发生变化、Slot迁移、主从切换等这些操作的信息需要同步给所有主节点,Gossip协议就是用来维护集群同步状态
RedisCluster是完全去中心化的,也就是没有一个统一的管理中心去通知所有节点进行同步,每个节点都是一个中心,RedisCluster采取的方案是使用Gossip协议来进行通信,节点之间通讯的目的是为了维护节点之间的元数据信息
Gossip:中文名称是流言协议,在此协议中包含了多种消息类型,即ping、pong、meet、fail等。原理就是节点之间不断的进行通信交换信息,一段时间后所有节点就都有了整个集群的完整信息,最终所有节点的状态都会达成一致。Gossip中除了fail消息是立即全网感知,其他消息都具有一定延迟,所以是最终一致性
节点之间通信流程
新节点向集群中的任意一个节点发送 meet 命令,并将自己的 IP 和端口号信息附在命令中
收到 meet 命令的节点会将新节点的信息记录下来,并向新节点返回 pong 命令,表示已经收到了新节点的请求
新节点收到 pong 命令后,将收到 pong 命令的节点加入到自身的节点列表中,并向此节点发送 ping 命令,等待其返回 pong 命令
当新节点收到足够数量的 pong 命令后,就会将自己加入到集群中,并向其他节点发送自己的拓扑结构信息。这个拓扑结构信息包括节点自身的信息以及其他节点的信息,以及每个节点的 hash slot 分配信息等
集群中的其他节点收到新节点的拓扑结构信息后,会将其添加到自己的节点列表中,并向新节点返回 pong 命令
新节点收到其他节点的 pong 命令后,就可以和其他节点进行通信,并开始接收和处理请求了。
每个Redis节点会开放两个端口号,一个是自身的运行端口号,另外一个就是运行端口号+10000,此端口号用于节点之间通信
节点之间建立TCP通道:每个节点都会与集群中的其他节点建立TCP连接,用于节点之间的通信。使用ping/pong消息保持节点之间的心跳连接
节点之间进行握手:每个节点在连接到其他节点后,在固定的时间间隔内,会随机选择的若干个节点发送ping消息,通知对方自己还在运行,并请求对方发送关于自己和其他节点的信息。如果节点长时间没有响应,则被认为已经失效,会向其他节点广播 Fail 消息,以便其他节点更新该节点的状态信息
节点响应:接收到ping消息的节点,会向发送ping消息的节点响应PONG命令,进行版本号和槽分配等元数据信息的交换,以确保集群中的所有节点拥有相同的元数据信息。并更新自己的局部视图
节点之间进行数据同步:当某个节点的主节点进行数据修改时,会将修改信息发送给所有从节点。从节点收到信息后,会进行数据同步,确保数据的一致性
节点之间进行故障转移:每个节点都会定期向其他节点发送PING命令,以确认对方是否存活。如果一个节点在一定时间内没有响应PING命令,其他节点就会将其标记为下线节点,并进行主从切换等操作
4.2.Gossip优缺点
Gossip优点
分布式高效:Gossip协议是一种去中心化的协议,节点之间相互交流信息,每个节点都可以通过传播信息来实现全局一致性,不需要中央控制节点,使得节点加入或退出集群更加高效
可伸缩性:Gossip协议可以很好地适应不同规模的系统,当节点数目增加时,节点间通信的成本是对数级别的
容错性:Gossip协议具有一定的容错能力,由于每个节点可以通过交互信息来更新状态,因此即使一部分节点失效,其他节点仍然可以更新状态,保持整个集群的一致性
自适应性:Gossip协议在传输信息时会根据实时情况进行调整,根据反馈信息和可靠性要求,自动选择合适的节点进行信息交流,从而提高了信息传输的效率和可靠性
低延迟:Gossip协议采用分散的信息传播方式,信息可以在整个网络中快速地传播,从而使得系统的响应速度更快
Gossip缺点
延迟问题:由于Gossip协议的传播速度相对较慢,因此可能存在节点状态更新的延迟问题。特别是在网络拓扑结构较为复杂或节点数量较大时,这种延迟问题会更加突出
带宽开销:由于Gossip协议的信息需要在节点之间不断传播,因此可能会产生较大的网络带宽开销。特别是在节点数量较大时,这种开销会更加严重
数据一致性问题:由于Gossip协议是基于随机的节点通信机制实现的,因此可能会出现数据不一致的情况。特别是在节点状态频繁变化时,这种问题会更加明显
安全性问题:Gossip协议需要在节点之间传递敏感信息,因此存在安全性问题。特别是在没有适当的加密和认证机制时,这种问题会更加严重
4.3.Gossip消息分类
ping:每个节点会按照固定时间频繁的给其他节点发送ping消息,集群节点互相通过ping交换元数据
pong:此消息是作为meet和ping的元数据响应消息
meet:新节点使用此消息通知任意一个集群节点,集群节点会响应pong消息邀请加入到集群
fail:某个节点判断另外一个节点宕机之后,会广播fail消息,通知其他节点此节点宕机的消息,其他节点接收到消息后标记此节点下线,四种消息中只有fail消息是立即全网感知,其他消息都具有一定的延迟
5.1.基于重定向的方式
连接RedisCluster集群时,可以使用Redis自带的redis-cli客户端,支持重定向,他有两种方式手动重定向和自动重定向
这种模式下,客户端将请求发送到集群中的某个节点,如果这个节点不是数据所在的节点,那么这个节点会将请求重定向到正确的节点上。在这个过程中,客户端和 Redis 集群节点之间使用 Redis 协议进行通信
默认情况下,如果节点响应MOVED,那么就意味着节点槽位错误,那么需要手动重新选择。如果是使用redis-cli客户端连接,那么也可以在连接时,指定 -c 属性,那么当响应MOVED时会自动重新连接到正确槽位节点
这种模式下,一般而言都最少需要重定向一次,当数据分布发生变化时,可能还需要重定向多次。重定向的网络开销是比较大的,所以一般推荐使用基于智能代理的方式来连接RedisCluster
5.2.基于智能代理的方式
智能代理的方式是近些年来才逐渐被 Redis Cluster 推崇的交互方式,其中Jedis的扩展客户端JedisCluster就是一个不错的选择,其他的还有Redisson、Lettuce
JedisCluster它的设计理念是为了提供更好的性能、可靠性和易用性。JedisCluster在Jedis的基础上增加了自动重定向、连接池等功能,并进行了性能优化和bug修复。同时,它也保留了Jedis的API和用法,使得用户可以很容易地迁移到Smart Jedis上来
JedisCluster实现原理
在初始化时,JedisCluster客户端会获取Redis Cluster中所有主节点的信息,包括节点IP地址、端口号和节点的槽位范围等信息,并将这些信息存储在本地的节点列表中
JedisCluster客户端在本地创建一个【槽位/节点映射表】,用来缓存集群中槽位与节点之间的映射关系。这个表的初始化过程如下:
当客户端发起请求时,JedisCluster客户端会先通过CRC16算法计算出请求对应的槽位
JedisCluster客户端会在本地的【槽位/节点映射表】中查找对应的节点,如果能够找到,则直接访问该节点并进行读写操作,避免了根据错误节点返回的MOVE指令进行重定向的开销
如果发生了数据迁移导致某个请求返回MOVE指令时,客户端会根据MOVE指令中返回的新节点信息,同步更新本地的【槽位/节点映射表】,以保证后续的请求能够直接通过该表进行节点定位,提升效率
为了避免出现网络异常等情况导致本地的【槽位/节点映射表】过期,JedisCluster客户端会定时从Redis Cluster中获取最新的节点信息,并更新本地的节点列表和【槽位/节点映射表】
通过这种方式,JedisCluster客户端能够实现高效的请求路由和自动重定向,同时减少了重定向的开销
扩容
缩容
ASK与PONG的区别
ASK 消息是指当某个节点在进行槽位迁移时,如果有客户端请求的数据位于正在迁移的槽位上,该节点会返回一个 ASK 消息给客户端,告诉客户端该数据已经被迁移到了哪个节点上,并让客户端去请求正确的节点。因此,ASK 消息是在数据请求过程中被主动发送的
PONG 消息则是在 Redis Cluster 节点间进行心跳检测时使用的。节点间通过相互发送 PING/PONG 消息来保持连接和同步信息,PONG 消息用于回复对方的 PING 消息,以保证节点之间的连接正常。因此,PONG 消息是在节点间进行通信时被动发送的
# 是否开启保护模式,yes时外部访问redis需要配置bind ip或访问密码,线上建议开启
protected-mode no
# 每个redis的端口都需要不同
port 6379
# 是否开启集群
cluster-enabled yes
# 该参数指定了集群配置文件的位置。每个节点在运行过程中,会维护一份集群配置文件
# 每当集群信息发生变化时比如增删节点,集群内所有节点会将最新信息更新到该配置文件
# 当节点重启后,会重新读取该配置文件,获取集群信息,可以方便的重新加入到集群中
# 此集群配置文件redis节点会自行维护,注意修改端口号
cluster-config-file nodes-6379.conf
# 集群节点不可用的最大时间,单位是毫秒,如果主节点在指定时间内不可达,那么会进行故障转移
cluster-node-timeout 15000
# 云服务器上部署需指定公网ip,或者Linux系统中的Docker运行时需要指定宿主机Ip
# cluster-announce-ip 公网ip地址
# 开启AOF持久化
appendonly yes
# AOF持久化文件名称
appendfilename "appendonly.aof"
# 设置节点密码,注意所有节点密码必须一致
requirepass 123456
# 主节点连接密码,用于保护集群,保持与requirepass一致即可
masterauth 123456
复制代码
redis-server.exe redis.windows.conf
复制代码
redis-cli --cluster create 127.0.0.1:6379 127.0.0.1:6380 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384 --cluster-replicas 1 -a 123456
复制代码
# 连接集群节点,可以连接启动的任意节点 -a 是指定节点密码
redis-cli.exe -c -h 127.0.0.1 -p 6379 -a 123456
# 查看集群信息
cluster info
# 查看当前Redis节点
info replication
# 查看当前节点槽位
cluster nodes
复制代码
# 返回上一级
cd ..
# 进入home目录
cd home
# 创建RedisCluster文件夹
mkdir RedisCluster
# 进入RedisCluster文件夹
cd RedisCluster
# 创建六个文件夹并创建出6个配置文件
mkdir 7901 7902 7903 7904 7905 7906
# 进入到创建好的7901文件夹中
ls
cd 7901
复制代码
# 1.创建并编辑配置文件
vi redis.conf
# 2.点击i进入编辑状态,然后将下面的配置文件粘贴到redis.conf文件中,注意修改端口号
# 3.点击Esc退出输入
# 4.输入命令保存并退出注意有冒号::wq
# 5.执行 ls 查看是否创建成功
复制代码
# 是否开启保护模式,yes时外部访问redis需要配置bind ip或访问密码,线上建议开启
protected-mode no
# 主节点连接密码,用于保护集群,保持与requirepass一致即可
masterauth 123456
# 每个redis的端口都需要不同
port 7901
# 是否开启集群
cluster-enabled yes
# 该参数指定了集群配置文件的位置。每个节点在运行过程中,会维护一份集群配置文件
# 每当集群信息发生变化时比如增删节点,集群内所有节点会将最新信息更新到该配置文件
# 当节点重启后,会重新读取该配置文件,获取集群信息,可以方便的重新加入到集群中
# 此集群配置文件redis节点会自行维护,注意修改端口号
cluster-config-file nodes-7901.conf
# 集群节点不可用的最大时间,单位是毫秒,如果主节点在指定时间内不可达,那么会进行故障转移
cluster-node-timeout 15000
# 云服务器上部署需指定公网ip,此处设置Linux服务器Ip
cluster-announce-ip 192.168.2.79
# 集群节点映射端口
cluster-announce-port 7901
# 集群节点通信端口
cluster-announce-bus-port 17901
# 开启AOF持久化
appendonly yes
# AOF持久化文件名称
appendfilename "appendonly.aof"
# 设置节点密码,注意所有节点密码必须一致
requirepass 123456
# Redis后台运行,在docker中无需开启
daemonize no
复制代码
[root@localhost 7901]# cp redis.conf ../7902
[root@localhost 7901]# cp redis.conf ../7903
[root@localhost 7901]# cp redis.conf ../7904
[root@localhost 7901]# cp redis.conf ../7905
[root@localhost 7901]# cp redis.conf ../7906
复制代码
# 进入到其他五个节点文件夹中修改配置的端口
vi redis.conf
# 点击Esc 输入 :wq保存并退出
# 返回上一级编辑指定文件夹:vi ../7902/redis.conf
复制代码
version: "3"
# 定义服务可以多个
services:
redis-cluster:
image: redis:latest
command: redis-cli --cluster create 192.168.2.79:7901 192.168.2.79:7902 192.168.2.79:7903 192.168.2.79:7904 192.168.2.79:7905 192.168.2.79:7906 --cluster-replicas 1 --cluster-yes -a 123456
depends_on:
- redis-7901
- redis-7902
- redis-7903
- redis-7904
- redis-7905
- redis-7906
redis-7901: # 服务名称
image: redis:latest # 创建容器时所需的镜像
container_name: redis-7901 # 容器名称
restart: always # 容器总是重新启动
ports:
- 7901:7901
- 17901:17901
volumes: # 数据卷,目录挂载
- ./etc_rc.local:/etc/rc.local
- ./7901/redis.conf:/etc/redis/redis.conf
- ./7901/data:/data
command: ["redis-server", "/etc/redis/redis.conf"] # 覆盖容器启动后默认执行的命令
redis-7902: # 服务名称
image: redis:latest # 创建容器时所需的镜像
container_name: redis-7902 # 容器名称
restart: always # 容器总是重新启动
ports:
- 7902:7902
- 17902:17902
volumes: # 数据卷,目录挂载
- ./etc_rc.local:/etc/rc.local
- ./7902/redis.conf:/etc/redis/redis.conf
- ./7902/data:/data
command: ["redis-server", "/etc/redis/redis.conf"] # 覆盖容器启动后默认执行的命令
redis-7903: # 服务名称
image: redis:latest # 创建容器时所需的镜像
container_name: redis-7903 # 容器名称
restart: always # 容器总是重新启动
ports:
- 7903:7903
- 17903:17903
volumes: # 数据卷,目录挂载
- ./etc_rc.local:/etc/rc.local
- ./7903/redis.conf:/etc/redis/redis.conf
- ./7903/data:/data
command: ["redis-server", "/etc/redis/redis.conf"] # 覆盖容器启动后默认执行的命令
redis-7904: # 服务名称
image: redis:latest # 创建容器时所需的镜像
container_name: redis-7904 # 容器名称
restart: always # 容器总是重新启动
ports:
- 7904:7904
- 17904:17904
volumes: # 数据卷,目录挂载
- ./etc_rc.local:/etc/rc.local
- ./7904/redis.conf:/etc/redis/redis.conf
- ./7904/data:/data
command: ["redis-server", "/etc/redis/redis.conf"] # 覆盖容器启动后默认执行的命令
redis-7905: # 服务名称
image: redis:latest # 创建容器时所需的镜像
container_name: redis-7905 # 容器名称
restart: always # 容器总是重新启动
ports:
- 7905:7905
- 17905:17905
volumes: # 数据卷,目录挂载
- ./etc_rc.local:/etc/rc.local
- ./7905/redis.conf:/etc/redis/redis.conf
- ./7905/data:/data
command: ["redis-server", "/etc/redis/redis.conf"] # 覆盖容器启动后默认执行的命令
redis-7906: # 服务名称
image: redis:latest # 创建容器时所需的镜像
container_name: redis-7906 # 容器名称
restart: always # 容器总是重新启动
ports:
- 7906:7906
- 17906:17906
volumes: # 数据卷,目录挂载
- ./etc_rc.local:/etc/rc.local
- ./7906/redis.conf:/etc/redis/redis.conf
- ./7906/data:/data
command: ["redis-server", "/etc/redis/redis.conf"] # 覆盖容器启动后默认执行的命令
复制代码
docker-compose up -d
复制代码
# 进入7901容器
docker exec -it redis-7901 bash
# 连接7901节点
redis-cli -c -p 7901 -a 123456
# 查看集群信息
cluster info
# 查看当前Redis节点
info replication
# 查看当前节点槽位
cluster nodes
复制代码
Redis目前Java中最火热的三个客户端都是可以操作RedisCluster:Jedis、Redisson、Lettuce
SpringBoot高版本中将spring-boot-starter-data-redis默认客户端从Jedis替换到了Lettuce,所以如果我们想要使用JedisCluster,那么需要移除Lettuce依赖,并引入Jedis依赖
本文以SpringBoot中的JedisCluster集成进入RedisTemplate为例,演示Java当中如何使用JedisCluster操作RedisCluster
核心配置对象:SpringBoot项目会自动读取配置并为我们创建,所以我们只需要使用即可
JedisPoolConfig:用于配置连接池,包括最大连接数、最大空闲连接数、连接超时时间等等。连接池的作用是避免频繁创建和关闭连接,提高连接复用率和性能
RedisClusterConfiguration:用于配置Redis Cluster各个节点的信息,包括节点的IP地址和端口号
JedisConnectionFactory:连接工厂,它继承自RedisConnectionFactory,可以通过传入JedisPoolConfig和RedisClusterConfiguration来创建连接
RedisTemplate:它是一个泛型类,可以指定键和值的类型。通过JedisConnectionFactory创建出RedisTemplate Bean后,就可以通过它来进行各种Redis操作,如get、set、incr、zadd等等
默认情况下,RedisTemplate使用的是JdkSerializationRedisSerializer进行序列化,这种序列化方式不易读取并且会出现乱码。为了解决这个问题,可以使用其他序列化方式,如Jackson和Fastjson
org.springframework.boot
spring-boot-starter-parent
2.6.2
org.springframework.boot
spring-boot-starter-data-redis
io.lettuce
lettuce-core
redis.clients
jedis
3.6.1
com.fasterxml.jackson.core
jackson-databind
2.12.7.1
org.springframework.boot
spring-boot-starter-test
test
复制代码
server:
port: 8080
spring:
redis:
timeout: 5000 # 超时时间
database: 0 # 默认连接那个库
cluster: # 集群节点Ip+Port,多个以逗号隔开
nodes:
- 127.0.0.1:6379
- 127.0.0.1:6380
- 127.0.0.1:6381
- 127.0.0.1:6382
- 127.0.0.1:6383
- 127.0.0.1:6384
max-redirects: 3 # 重定向最大次数
jedis:
pool: # jedis连接池配置
max-active: 8 # 连接池中最大的活跃连接数,即同时能从连接池中获取的最大连接数
max-wait: -1 # 当连接池中没有可用连接时,调用者最大阻塞等待时间(单位为毫秒),超过这个时间后将抛出异常,-1为无限等待
max-idle: 8 # 连接池中最大的空闲连接数,即连接池中最多能保持多少个空闲连接,超过这个数目的空闲连接将被释放
min-idle: 0 # 接池中最小的空闲连接数,即连接池中保持的最少的空闲连接数,如果空闲连接数小于这个数目且总连接数小于 max-active,连接池就会创建新的连接
password: 123456 # 配置连接密码
复制代码
package cn.neuronet.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfiguration extends CachingConfigurerSupport {
@Autowired
private RedisConnectionFactory factory;
@Bean
public RedisTemplate redisTemplate() {
// 1.创建一个key是String,value是Object类型的RedisTemplate,使用时类型必须对应
RedisTemplate template = new RedisTemplate<>();
// 2.JedisConnectionFactory会使用RedisClusterConfiguration和JedisPoolConfig对象来创建JedisCluster连接池
template.setConnectionFactory(factory);
// 3.创建一个Jackson2JsonRedisSerializer对象,指定需要序列化和反序列化的对象类型为Object类型,替换默认的jdkSerializeable序列化
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
// 4.创建ObjectMapper对象,用于设置访问属性和默认类型
ObjectMapper om = new ObjectMapper();
// 5.设置所有访问属性可见,同时将对象的类型信息一起序列化到JSON串中
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
// 6.将ObjectMapper对象设置到Jackson2JsonRedisSerializer中
jackson2JsonRedisSerializer.setObjectMapper(om);
// 7.用于序列化Redis中的String类型的数据。它是RedisTemplate的默认key序列化器,将Rediskey从String类型序列化为字节数组,以便于存储到Redis中
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// 8.String数据类型的Key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
// 9.Hash数据类型的key也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// 10.String数据类型的value序列化方式采用jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
// 11.Hash数据类型的value序列化方式采用jackson
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
复制代码
package cn.neuronet;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.test.context.junit4.SpringRunner;
/**
* @Author: Neuronet
* @Date: 2023-04-23
* @Description: RedisCluster集群测试
* @Version:1.0
*/
@SpringBootTest
public class RedisClusterTest {
@Autowired
private RedisTemplate redisTemplate;
@Test
public void test() throws Exception{
redisTemplate.opsForValue().set("neuronet", "稀土掘金");
Object neuronetStr = redisTemplate.opsForValue().get("neuronet");
System.out.println(neuronetStr);
}
}
复制代码