Redis安装
Redis的主从、哨兵、集群功能
一、配置主从同步
slave 需要加上如下配置
1 | # master的ip和端口 |
redis的主从同步只能是一个主加上多个从,而且不能实现主节点挂了之后从从节点中获取一个作为新的主节点,并不符合高可用的定义。
如果要是实现高可用,还需要引入哨兵这一配置。
二、使用哨兵
官方文档 : http://www.redis.cn/topics/sentinel.html
redis哨兵机制的实现原理解析:https://www.cnblogs.com/knowledgesea/p/6567718.html
2.1 哨兵的作用
- 监控(Monitoring): 哨兵(sentinel) 会不断地检查你的Master和Slave是否运作正常。
- 提醒(Notification):当被监控的某个Redis节点出现问题时, 哨兵(sentinel) 可以通过 API 向管理员或者其他应用程序发送通知。
- 自动故障迁移(Automatic failover):当一个Master不能正常工作时,哨兵(sentinel) 会开始一次自动故障迁移操作,它会将失效Master的其中一个Slave升级为新的Master, 并让失效Master的其他Slave改为复制新的Master;当客户端试图连接失效的Master时,集群也会向客户端返回新Master的地址,使得集群可以使用现在的Master替换失效Master。Master和Slave服务器切换后,Master的redis.conf、Slave的redis.conf和sentinel.conf的配置文件的内容都会发生相应的改变,即,Master主服务器的redis.conf配置文件中会多一行slaveof的配置,sentinel.conf的监控目标会随之调换。
2.2 图解
2.3 工作机制
Redis提供了sentinel(哨兵)机制,通过sentinel模式启动redis后,自动监控master/slave的运行状态,基本原理是:心跳机制+投票裁决 。每个sentinel会向其它sentinal、master、slave定时发送消息,以确认对方是否“活”着,如果发现对方在指定时间(可配置)内未回应,则暂时认为对方已挂(所谓的“主观认为宕机” Subjective Down,简称SDOWN)。
若”哨兵群”中的多数sentinel,都报告某一master没响应,系统才认为该master”彻底死亡”(即:客观上的真正down机,Objective Down,简称ODOWN),通过一定的vote算法,从剩下的slave节点中,选一台提升为master,然后自动修改相关配置。
2.3.1 Sentinel的“仲裁会”
前面我们谈到,主从故障转移时,需要的sentinel认可的票数达到设置的值才可以。
不过,当failover主备切换真正被触发后,failover并不会马上进行,还需要sentinel中的大多数sentinel授权后才可以进行failover。
当sentinel认可不可用的票数达到时(ODOWN),failover被触发。failover一旦被触发,尝试去进行failover的sentinel会去获得“大多数”sentinel的授权(如果票数比大多数还要大的时候,则询问更多的sentinel)
这个区别看起来很微妙,但是很容易理解和使用。例如,集群中有5个sentinel,票数被设置为2,当2个sentinel认为一个master已经不可用了以后,将会触发failover,但是,进行failover的那个sentinel必须先获得至少3个sentinel的授权才可以实行failover。
如果票数被设置为5,要达到ODOWN状态,必须所有5个sentinel都主观认为master为不可用,要进行failover,那么得获得所有5个sentinel的授权。
2.3.2 配置版本号
为什么要先获得大多数sentinel的认可时才能真正去执行failover呢?
当一个sentinel被授权后,它将会获得宕掉的master的一份最新配置版本号,当failover执行结束以后,这个版本号将会被用于最新的配置。因为大多数sentinel都已经知道该版本号已经被要执行failover的sentinel拿走了,所以其他的sentinel都不能再去使用这个版本号。这意味着,每次failover都会附带有一个独一无二的版本号。我们将会看到这样做的重要性。
而且,sentinel集群都遵守一个规则:如果sentinel A推荐sentinel B去执行failover,B会等待一段时间后,自行再次去对同一个master执行failover,这个等待的时间是通过failover-timeout
配置项去配置的。从这个规则可以看出,sentinel集群中的sentinel不会再同一时刻并发去failover同一个master,第一个进行failover的sentinel如果失败了,另外一个将会在一定时间内进行重新进行failover,以此类推。
redis sentinel保证了活跃性:如果大多数sentinel能够互相通信,最终将会有一个被授权去进行failover.
redis sentinel也保证了安全性:每个试图去failover同一个master的sentinel都会得到一个独一无二的版本号。
2.3.3 配置传播
一旦一个sentinel成功地对一个master进行了failover,它将会把关于master的最新配置通过广播形式通知其它sentinel,其它的sentinel则更新对应master的配置。
一个faiover要想被成功实行,sentinel必须能够向选为master的slave发送SLAVE OF NO ONE
命令,然后能够通过INFO
命令看到新master的配置信息。
当将一个slave选举为master并发送`SLAVE OF NO ONE``后,即使其它的slave还没针对新master重新配置自己,failover也被认为是成功了的,然后所有sentinels将会发布新的配置信息。
新配在集群中相互传播的方式,就是为什么我们需要当一个sentinel进行failover时必须被授权一个版本号的原因。
每个sentinel使用##发布/订阅##的方式持续地传播master的配置版本信息,配置传播的##发布/订阅##管道是:__sentinel__:hello
。
因为每一个配置都有一个版本号,所以以版本号最大的那个为标准。
举个栗子:假设有一个名为mymaster的地址为192.168.1.50:6379。一开始,集群中所有的sentinel都知道这个地址,于是为mymaster的配置打上版本号1。一段时候后mymaster死了,有一个sentinel被授权用版本号2对其进行failover。如果failover成功了,假设地址改为了192.168.1.50:9000,此时配置的版本号为2,进行failover的sentinel会将新配置广播给其他的sentinel,由于其他sentinel维护的版本号为1,发现新配置的版本号为2时,版本号变大了,说明配置更新了,于是就会采用最新的版本号为2的配置。
这意味着sentinel集群保证了第二种活跃性:一个能够互相通信的sentinel集群最终会采用版本号最高且相同的配置。
2.3.4 SDOWN和ODOWN的更多细节
sentinel对于不可用有两种不同的看法,一个叫主观不可用(SDOWN),另外一个叫客观不可用(ODOWN)。
SDOWN是sentinel自己主观上检测到的关于master的状态。
ODOWN需要一定数量的sentinel达成一致意见才能认为一个master客观上已经宕掉,各个sentinel之间通过命令 **SENTINEL is_master_down_by_addr
**来获得其它sentinel对master的检测结果。
从sentinel的角度来看,如果发送了PING心跳后,在一定时间内没有收到合法的回复,就达到了SDOWN的条件。这个时间在配置中通过 **is-master-down-after-milliseconds**
参数配置。
当sentinel发送PING后,以下回复都被认为是合法的,除此之外,其它任何回复(或者根本没有回复)都是不合法的。
1 | PING replied with +PONG. |
从SDOWN切换到ODOWN不需要任何一致性算法,只需要一个gossip协议:如果一个sentinel收到了足够多的sentinel发来消息告诉它某个master已经down掉了,SDOWN状态就会变成ODOWN状态。如果之后master可用了,这个状态就会相应地被清理掉。
正如之前已经解释过了,真正进行failover需要一个授权的过程,但是所有的failover都开始于一个ODOWN状态。
ODOWN状态只适用于master,对于不是master的redis节点sentinel之间不需要任何协商,slaves和sentinel不会有ODOWN状态。
2.3.5 Sentinel之间和Slaves之间的自动发现机制
虽然sentinel集群中各个sentinel都互相连接彼此来检查对方的可用性以及互相发送消息。但是你不用在任何一个sentinel配置任何其它的sentinel的节点。因为sentinel利用了master的发布/订阅机制去自动发现其它也监控了统一master的sentinel节点。
通过向名为__sentinel__:hello
的管道中发送消息来实现。
同样,你也不需要在sentinel中配置某个master的所有slave的地址,sentinel会通过询问master来得到这些slave的地址的。
每个sentinel通过向每个master和slave的发布/订阅频道__sentinel__:hello
每秒发送一次消息,来宣布它的存在。
每个sentinel也订阅了每个master和slave的频道__sentinel__:hello
的内容,来发现未知的sentinel,当检测到了新的sentinel,则将其加入到自身维护的master监控列表中。
每个sentinel发送的消息中也包含了其当前维护的最新的master配置。如果某个sentinel发现
自己的配置版本低于接收到的配置版本,则会用新的配置更新自己的master配置。
在为一个master添加一个新的sentinel前,sentinel总是检查是否已经有sentinel与新的sentinel的进程号或者是地址是一样的。如果是那样,这个sentinel将会被删除,而把新的sentinel添加上去。
2.4 哨兵的配置文件
1 | # 端口 |
启动命令
1 | .\redis-server.exe .\sentinel.conf --sentinel |
2.4 springboot使用
修改下配置文件即可
1 | spring: |
然后正常使用即可。
经过测试,所有节点只剩一个节点存活依然可以保持服务的正常运行
2.5 一些需要知道的东西
- 一个健壮的部署至少需要三个哨兵实例。
- 三个哨兵实例应该放置在客户使用独立方式确认故障的计算机或虚拟机中。例如不同的物理机或不同可用区域的虚拟机。
- sentinel + Redis实例不保证在故障期间保留确认的写入,因为Redis使用异步复制。然而有方式部署哨兵使丢失数据限制在特定时刻,虽然有更安全的方式部署它。
- 你的客户端要支持哨兵,流行的客户端都支持哨兵,但不是全部。
- 没有HA设置是安全的,如果你不经常的在开发环境测试,在生产环境他们会更好。你可能会有一个明显的错误配置只是当太晚的时候。
- Sentinel,Docker,或者其他形式的网络地址交换或端口映射需要加倍小心:Docker执行端口重新映射,破坏Sentinel自动发现其他的哨兵进程和master的slave列表。稍后在这个文档里检查关于Sentinel和Docker的部分,了解更多信息。
2.6 哨兵部署示例
例1,只有两个Sentinel,不要这样做
1 | +----+ +----+ |
- 在这个设置中,如果master M1故障,R1将被晋升因为两个Sentinel可以达成协议并且还可以授权一个故障转移因为多数就是两个。所以他表面上看起来是可以工作的,然而检查下一个点了解为什么这个设置是不行的。
- 如果运行M1的盒子停止工作了,S1也停止工作。运行在其他盒子上的S2将不能授权故障转移,所以系统将变成不可用。
注意为了排列不同的故障转移需要少数服从多数,并且稍后向所有的Sentinel传播最新的配置。还要注意上面配置的故障转移的能力,没有任何协定,非常危险:
1 | +----+ +------+ |
在上面的配置中我们使用完美的对称方式创建了两个master(假定S2可以在未授权的情况下进行故障转移)。客户端可能会不确定往哪边写,并且没有途径知道什么时候分区配置是正确的,为了预防一个永久的断裂状态。
所有请永远部署至少三个Sentinel在三个不同的盒子里。
例2:使用三个盒子的基本设置
这是个非常简单的设置,它有简单调整安全的优势。它基于三个盒子,每个盒子同时运行一个Redis实例和一个Sentinel实例。
1 | +----+ |
如果M1故障,S2和S3将会商定故障并授权故障转移,使客户端可以继续。
在每个Sentinel设置里,Redis是异步主从复制,总会有丢失数据的风险,因为有可能当它成为master的时候,一个确认的写入操作还没有同步到slave。然后在上面的设置中有一个更高的风险由于客户端分区一直是老的master,就像下面的图像所示:
1 | +----+ |
在这个案例中网络分区隔离老的master M1,所以slave R2晋升为master。然而客户端,比如C1,还在原来的老的master的分区,可能继续往老master写数据。这个数据将会永久丢失,因为分区恢复时,master将会重新配置为新master的slave,丢弃它的数据集。
这个问题可以使用下面的Redis主从复制特性减轻,它可在master检查到它不再能传输它的写入操作到指定数量的slave的时候停止接收写入操作。
1 | min-slaves-to-write 1 |
使用上面的配置(请查看自带的redis.conf示例了解更多信息)一个Redis实例,当作为一个master,如果它不能写入至少1个slave将停止接收写入操作。(N个Slave以上不通就停止接收)
由于主从复制是异步的不能真实的写入,意味着slave断开连接,或者不再向我们发送异步确认的指定的max-lag秒数。(判定连接不通的超时时间)
在上面的示例中使用这个配置,老master M1将会在10秒钟后变为不可用。当分区恢复时,Sentinel配置将指向新的一个,客户端C1将能够获取到有效的配置并且将使用新master继续工作。
然而天下没有免费的午餐,这种改进,如果两个slave挂掉,master将会停止接收写入操作。这是个权衡。
例三:Sentinel在客户端盒子里
有时我们只有两个Redis盒子可用,一个master和一个slave。在例二中的配置在那样的情况下是不可行的,所谓我们可以借助下面的,Sentinel放置在客户端:
1 | +----+ +----+ |
在这个设置里,Sentinel的视角和客户端的视角相同:如果大多数的客户端认为master是可以到达的,它就是好的。C1,C2,C3是一般的客户端,这不意味着C1识别单独的客户端连接到Redis。它更像一些如应用服务,Rails应用之类的。
如果运行M1和S1的盒子故障,故障转移将会发生,然而很容看到不同的网络分区将导致不同的行为。例如如果客户端和Redis服务之间的断开连接,Sentinel将不能设置,因为master和slave将都不可用。
注意如果使用M1获取分区,我们有一个和例二中描述的相似的问题,不同的是这里我们没有办法打破对称,由于只有一个slave和master,所以当它的master断开连接时master不能停止接收查询,否则在slave故障期间master将永不可用。
所以这是个有效的设置但是在例二中的设置有像更容易管理HA系统的优点, 并有能力限制master接收写入的时间。
例4:少于3个客户端的Sentinel客户端
在例3中如果客户端少于3个就不能使用。在这个案例中我们使用一个混合的设置:
1 | +----+ +----+ |
这里和例3非常类似,但是这里我们在4个盒子里运行四个哨兵。如果M1故障其他的三个哨兵可以执行故障转移。
三、redis集群
3.1 redis集群介绍
Redis集群是一个由多个主从节点群组成的分布式服务集群,它具有复制、高可用和分片特性。Redis集群不需要sentinel哨兵也能完成节点移除和故障转移的功能。需要将每个节点设置成集群模式,这种集群模式没有中心节点,可水平扩展,据官方文档称可以线性扩展到上万个节点(官方推荐不超过1000个节点)。redis集群的性能和高可用性均优于之前版本的哨兵模式,且集群配置非常简单。
3.2 Redis集群的优点:
(1)Redis集群有多个master,可以减小访问瞬断问题的影响;
若集群中有一个master挂了,正好需要向这个master写数据,这个操作需要等待一下;但是向其他master节点写数据是不受影响的。
(2)Redis集群有多个master,可以提供更高的并发量;
(3)Redis集群可以分片存储,这样就可以存储更多的数据;
3.3 Redis集群的搭建
Redis的集群搭建最少需要3个master节点,我们这里搭建3个master,每个下面挂一个slave节点,总共6个Redis节点;(3台机器,每台机器一主一从)
每个redis都需要加上以下配置信息
1 | #启动集群模式 |
使用redis_cli命令工具创建集群
1 | /usr1/redis/redis-5.0.3/src/redis-cli -a redis-pw --cluster create --cluster-replicas 1 192.168.1.1:8001 192.168.1.1:8002 192.168.1.2:8001 192.168.1.2:8002 192.168.1.3:8001 192.168.1.3:8002 |
3.4 springboot 连接集群
只需要对配置文件做些修改即可
1 | spring: |
使用redisTemplate操作即可。
3.5 Redis集群原理分析
Redis Cluster 将所有数据划分为 16384 个 slots(槽位),每个节点负责其中一部分槽位。槽位的信息存储于每个节点中。只有master节点会被分配槽位,slave节点不会分配槽位。
当Redis Cluster 的客户端来连接集群时,它也会得到一份集群的槽位配置信息,并将其缓存在客户端本地。这样当客户端要查找某个 key 时,可以直接定位到目标节点。同时因为槽位的信息可能会存在客户端与服务器不一致的情况,还需要纠正机制来实现槽位信息的校验调整。
槽位定位算法
Cluster 默认会对 key 值使用 crc16 算法进行 hash 得到一个整数值,然后用这个整数值对 16384 进行取模来得到具体槽位。
HASH_SLOT = CRC16(key) % 16384
跳转重定位
当客户端向一个节点发出了指令,首先当前节点会计算指令的 key 得到槽位信息,判断计算的槽位是否归当前节点所管理;若槽位不归当前节点管理,这时它会向客户端发送一个特殊的跳转指令携带目标操作的节点地址,告诉客户端去连这个节点去获取数据。客户端收到指令后除了跳转到正确的节点上去操作,还会同步更新纠正本地的槽位映射表缓存,后续所有 key 将使用新的槽位映射表。
Redis集群节点之间的通信机制
维护集群的元数据有集中式和 gossip两种方式,Redis 的集群节点之间的通信采取 gossip 协议进行通信
(1)集中式:
优点:元数据的更新和读取,时效性非常好,一旦元数据出现变更立即就会更新到集中式的存储中,其他节点读取的时候立即就可以立即感知到;
缺点:所有的元数据的更新压力全部集中在一个地方,可能导致元数据的存储压力。zookeeper使用该方式
(2)gossip:
gossip协议包含多种消息,包括ping,pong,meet,fail等等。。
优点:元数据的更新比较分散,不是集中在一个地方,更新请求会陆陆续续,打到所有节点上去更新,有一定的延时,降低了压力;
缺点:元数据更新有延时可能导致集群的一些操作会有一些滞后。
每个节点都有一个专门用于节点间通信的端口,就是自己提供服务的端口号+10000,比如7001,那么用于节点间通信的就是17001端口。 每个节点每隔一段时间都会往另外几个节点发送ping消息,同时其他几点接收到ping消息之后返回pong消息。
网络抖动
网络抖动就是非常常见的一种现象,突然之间部分连接变得不可访问,然后很快又恢复正常。
为解决这种问题,Redis Cluster 提供了一种选项 cluster-node-timeout ,表示当某个节点失联的时间超过了配置的 timeout时,才可以认定该节点出现故障,需要进行主从切换。如果没有这个选项,网络抖动会导致主从频繁切换 (数据的重新复制)。
Redis集群选举原理
当 slave 发现自己的 master 变为 fail 状态时,便尝试进行 FailOver,以期成为新的 master。由于挂掉的 master 有多个 slave,所以这些 slave 要去竞争成为 master 节点,过程如下:
(1)slave1,slave2都 发现自己连接的 master 状态变为 Fail;
(2)它们将自己记录的集群 currentEpoch(选举周期) 加1,并使用 gossip 协议去广播 FailOver_auth_request 信息;
(3)其他节点接收到slave1、salve2的消息(只有master响应),判断请求的合法性,并给 slave1 或 slave2 发送 FailOver_auth_ack(对每个 epoch 只发一次ack); 在一个选举周期中,一个master只会响应第一个给它发消息的slave;
(4)slave1 收集返回的 FailOver_auth_ack,它收到超过半数的 master 的 ack 后变成新 master; (这也是集群为什么至少需要3个master的原因,如果只有两个master,其中一个挂了之后,只剩下一个主节点是不能选举成功的)
(5)新的master节点去广播 Pong 消息通知其他集群节点,不需要再去选举了。
从节点并不是在主节点一进入 FAIL 状态就马上尝试发起选举,而是有一定延迟,一定的延迟确保我们等待FAIL状态在集群中传播,slave如果立即尝试选举,其它masters或许尚未意识到FAIL状态,可能会拒绝投票。
延迟计算公式:DELAY = 500ms + random(0 ~ 500ms) + SLAVE_RANK * 1000ms
SLAVE_RANK:表示此slave已经从master复制数据的总量的rank。Rank越小代表已复制的数据越新。这种方式下,持有最新数据的slave将会首先发起选举(理论上)
Redis集群为什么至少需要三个master节点,并且推荐节点数为奇数?
因为新master的选举需要大于半数的集群master节点同意才能选举成功,如果只有两个master节点,当其中一个挂了,是达不到选举新master的条件的。
奇数个master节点可以在满足选举该条件的基础上节省一个节点,比如三个master节点和四个master节点的集群相比,大家如果都挂了一个master节点都能选举新master节点,如果都挂了两个master节点都没法选举新master节点了,所以奇数的master节点更多的是从节省机器资源角度出发说的。
集群是否完整才能对外提供服务
当redis.conf的配置cluster-require-full-coverage为no时,表示当负责一个插槽的主库下线且没有相应的从库进行故障恢复时,集群仍然可用,如果为yes则集群不可用。
四、主从、哨兵、集群 的区别
4.1 主从
主从一般都用作灾备来使用,从服务器能及时的将主服务器的数据进行一个备份。
4.2 哨兵
哨兵强调的是高可用,Sentinel 系统用于管理多个 Redis 服务器(instance), 该系统执行以下三个任务:
监控(Monitoring): Sentinel 会不断地检查你的主服务器和从服务器是否运作正常。
提醒(Notification): 当被监控的某个 Redis 服务器出现问题时, Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。
自动故障迁移(Automatic failover): 当一个主服务器不能正常工作时, Sentinel 会开始一次自动故障迁移操作, 它会将失效主服务器的其中一个从服务器升级为新的主服务器, 并让失效主服务器的其他从服务器改为复制新的主服务器; 当客户端试图连接失效的主服务器时, 集群也会向客户端返回新主服务器的地址, 使得集群可以使用新主服务器代替失效服务器。
客户端中不会记录redis的地址(某个IP),而是记录sentinel的地址,这样我们可以直接从sentinel获取的redis地址,因为sentinel会对所有的master、slave进行监控,它是知道到底谁才是真正的master的,例如我们故障转移,这时候对于sentinel来说,master是变了的,然后通知客户端。而客户端根本不用关心到底谁才是真正的master,只关心sentinel告知的master。
4.3 集群
集群模式提高并发量。
即使使用哨兵,redis每个实例也是全量存储,每个redis存储的内容都是完整的数据,浪费内存且有木桶效应。为了最大化利用内存,可以采用集群,就是分布式存储。即每台redis存储不同的内容,共有16384个slot。每个redis分得一些slot,hash_slot = crc16(key) mod 16384 找到对应slot,键是可用键,如果有{}则取{}内的作为可用键,否则整个键是可用键
集群至少需要3主3从,且每个实例使用不同的配置文件,主从不用配置,集群会自己选。
cluster是为了解决单机Redis容量有限的问题,将数据按一定的规则分配到多台机器。
附件:参考文章
https://www.cnblogs.com/knowledgesea/p/6567718.html
http://www.redis.cn/topics/sentinel.html
https://www.cnblogs.com/knowledgesea/p/6567718.html