虽然Redis有持久化功能保障Redis服务器宕机也能恢复并且只有少量数据损失,但由于所有数据都在一台服务器上,如果服务器出现硬盘故障,那就算有备份也不能避免数据丢失的问题。而且在实际生产环境中,我们不能只使用一台Redis服务器作为缓存服务器,必须要多台服务器,
避免单点故障。
主从复制
复制的作用是把Redis的数据复制多个副本部署在不同的服务器上,如果其中一台服务器出现故障,也能快速迁移到其他服务器上提供服务。主从复制就是我们常见的master/slave模式,主服务器可以进行读写操作,当写操作导致数据发生变化时会自动将数据同步给从服务器。从数据库是只读的,并接收主服务器同步过来的数据。一个主服务器可以有多个从服务器。
配置实现
为Redis配置master/slave模式是非常容易的,
只要在从服务器的配置文件中加入 slaveof 主服务器地址端口,而master服务器不需要任何改变。
步骤:1.准备两台服务器server1(主)、server2(从),分别安装Redis;2.在server2的redis.conf文件中增加slaveof server1-ip 6379,同时将bindip注释掉,允许所有IP访问;3.启动server2,访问server2的redis客户端,输入"INFO replication”查看主从信息;4. 通过在master机器上输入命令,比如"set foo bar"、 在slave服务器就能看到该值已经同步过来了;
原理
-
全量复制
Redis全量复制一般发生在slave初始化阶段,这时slave需要将master上的所有数据都复制一份。具体步骤:
1.从服务器连接主服务器,并发送SYNC命令;2.主服务器接收到命令后执行BGSAVE生成快照,并记录生成快照期间的写命令;3.主服务器将生成的快照文件发送给从服务器;4.从服务器载入快照文件;5.主服务器将缓存的写命令发送给从服务器;6.从服务器执行主服务器发送过来的缓存的写命令;
流程图如下:
完成上面几个步骤后就完成了slave服务器数据初始化的所有操作,slave服务器此时可以接收来自用户的读请求。
master/slave
复制策略是采用乐观复制(最终一致性),也就是说可以容忍短时间内master/slave数据的内容是不一致的,但是两者的数据会最终同步。具体来说,redis的
主从同步过程本身是异步的,意味着master执行完客户端请求的命令后会立即返回结果给客户端,然后异步的方式把命令同步给slave。这一特征保证启用master/slave后master的性能不会受到影响。但另一方面,如果在这个数据不一致的窗口期间,master/slave因为网络问题断开连接,而这个时候,master是无法得知某个命令最终同步给了多少个slave数据库。不过redis提供了一个配置项来限制只有数据至少同步给多少个slave的时候,master才是可写的:
min-slaves-to-write 3 //表示只有当3个或以上的slave连接到master,master才是可写的;min-slaves-max-lag 10 //表示允许slave最长失去连接的时间,如果10秒还没收到slave的响应,则master认为该slave已断开;
-
增量 复制
从redis 2.8开始,就
支持主从复制的断点续传。如果主从复制过程中网络连接断掉了,可以接着上次复制的地方继续复制下去,而不是从头开始复制一份。master node会在内存中创建一个backlog,master和slave都会保存一个replica offset还有一个master id,offset就是保存在backlog中的。如果master和slave网络连接断掉了,slave会让master从上次的replica offset开始继续复制,但是如果没有找到对应的offset,那么就会执行一次全量同步。
-
无硬盘复制
前面说过,Redis复制的工作原理基于RDB方式的持久化实现的,也就是master在后台保存RDB快照,slave接收到rdb文件并载入,但是这种方式会存在一些问题。
1.当master禁用RDB时,如果执行了复制初始化操作,Redis依然会生成RDB快照,当master下次启动时执行该RDB文件的恢复,但是因为复制发生的时间点不确定,所以恢复的数据可能是任何时间点的,就会造成数据出现问题;2.当硬盘性能比较慢的情况下(网络硬盘),那初始化复制过程会对性能产生影响;
因此Redis2.8.18以后的版本引入了无硬盘复制选项,可以不需要通过RDB文件去同步,直接发送数据,通过以下配置来开启该功能:
repl-diskless-sync yes
master在内存中直接创建RDB,然后发送给slave,不会在本地落地磁盘了。
说明:
当需要在一台服务器上搭建伪集群时,可以将配置文件复制多个副本,每个副本为每个节点的配置文件,然后通过指定配置文件的方式启动服务端(配置文件内设置已守护进程方式运行)。例如Mac通过HomeBrew方式安装的Redis,安装后会自动配置环境变量(可以直接通过redis-server命令启动服务端)。可以找到安装Redis时创建的plist文件,通过plist文件可以看到redis.conf配置文件的位置,将该文件复制多份到指定位置,并修改各个节点的配置文件,然后通过redis-server ${redis.conf}命令启动各个节点。
哨兵机制
在典型的一主多从的master/slave模式系统中,
slave在整个体系中起到了数据冗余备份和读写分离的作用。当master遇到异常中断后,需要从slave中选举一个新的master继续对外提供服务。比如在zk中通过leader选举、kafka中可以基于zk的节点实现master选举。所以在redis中也需要一种机制去实现master的决策,redis并没有提供自动master选举功能,而是需要借助一个哨兵来进行监控。
哨兵的作用就是监控Redis系统的运行状况,它的功能包括两个:
1. 监控master和slave是否正常运行;2. master出现故障时自动将slave数据库升级为master;
哨兵是一个独立的进程,使用哨兵后的架构图:
为了解决master选举问题,又引出了一个单点问题,也就是哨兵的可用性如何解决,在一个一主多从的Redis系统中,可以使用多个哨兵进行监控任务以保证系统足够稳定。此时
哨兵不仅会监控master和slave,同时还会互相监控;这种方式称为哨兵集群,哨兵集群需要解决故障发现和master决策的协商机制问题。
sentinel之间相互感知
sentinel节点之间会因为共同监视同一个master从而产生关联,一个新加入的sentinel节点需要和其他监视相同master节点的sentinel相互感知。
感知过程:1. 需要相互感知的sentinel都向他们共同监视的master节点订阅channel:sentinel:hello。2. 新加入的sentinel节点向这个channel发布一条消息,包含自己本身的信息,这样订阅了这个channel的sentinel就可以发现这个新的sentinel。3. 新加入得sentinel和其他sentinel节点建立长连接。
sentinel感知原理图:
master的故障发现
sentinel节点会
定期向master节点发送心跳包来判断存活状态,一旦master节点没有正确响应,sentinel会把master设置为“主观不可用状态”,然后它会把“主观不可用”发送给其他所有的sentinel节点去确认,当确认的sentinel节点数大于
quorum时,则会认为master是“客观不可用”,接着就开始进入选举新的master流程;但是这里又会遇到一个问题,就是sentinel中,本身是一个集群,如果多个节点同时发现master节点达到客观不可用状态,那谁来决策选择哪个节点作为maste呢?这个时候就需要从sentinel集群中选择一个leader来做决策。而这里用到了一致性算法Raft算法,它和Paxos算法类似,都是分布式一致性算法。但是它比Paxos算法要更容易理解; Raft和Paxos算法一样,也是基于投票算法,只要保证过半数节点通过提议即可;(Raft算法流程动画演示地址:
http://thesecretlivesofdata.com/raft/)
配置实现
通过在这个配置的基础上增加哨兵机制。在其中任意一台服务器或多台服务器上创建一个sentinel.conf文件,文件内容:
port <port>//当前Sentinel服务运行的端口;logfile <logfile-full-path>//日志文件位置daemonize yes//添加为后台运行dir <dir>//Sentinel服务运行时使用的临时文件夹;sentinel monitor <master-name> <ip> <port> <quorum>//指定redis的master节点名称(自定义)、地址、端口、客观认定数量;quorum表示需要几个哨兵节点确认才可将master的状态从“主观不可用”变为“客观不可用”;sentinel auth-pass <master-name> <password>//设置连接master和slave时的密码,注意的是sentinel不能分别为master和slave设置不同的密码,因此master和slave的密码应该设置相同,如果没有密码可以不设置该配置项;sentinel down-after-milliseconds <master-name> <time>//Sentinel认为Redis实例已经失效所需的毫秒数;sentinel failover-timeout <master-name> <time>//表示如果在该时间(ms)后,master仍没活过来,则启动failover,从剩下的slave中选一个升级为master;
两种方式启动哨兵
redis-sentinel sentinel.confredis-server /path/to/sentinel.conf --sentinel
哨兵监控一个系统时,
只需要配置监控master即可,哨兵会自动发现所有slave;这时候,我们把master关闭,等待指定时间后(默认是30秒),会自动进行切换,会输出如下消息:
coderX@macbook-pro:~$ redis-sentinel s1.conf9127:X 22 Aug 23:27:50.999 # You requested maxclients of 10000 requiring at least 10032 max file descriptors.9127:X 22 Aug 23:27:50.999 # Server can't set maximum open files to 10032 because of OS error: Operation not permitted.9127:X 22 Aug 23:27:50.999 # Current maximum open files is 4096. maxclients has been reduced to 4064 to compensate for low ulimit. If you need higher maxclients increase 'ulimit -n'._.__.-``__ ''-.__.-`` `. `_. ''-._ Redis 3.2.1 (00000000/0) 64 bit.-`` .-```. ```\/ _.,_ ''-._( ' , .-` | `, ) Running in sentinel mode|`-._`-...-` __...-.``-._|'` _.-'| Port: 26379| `-._ `._ / _.-' | PID: 9127`-._ `-._ `-./ _.-' _.-'|`-._`-._ `-.__.-' _.-'_.-'|| `-._`-._ _.-'_.-' | http://redis.io`-._ `-._`-.__.-'_.-' _.-'|`-._`-._ `-.__.-' _.-'_.-'|| `-._`-._ _.-'_.-' |`-._ `-._`-.__.-'_.-' _.-'`-._ `-.__.-' _.-'`-._ _.-'`-.__.-'9127:X 22 Aug 23:27:51.000 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.9127:X 22 Aug 23:27:51.003 # Sentinel ID is 26f43074868559a81927040f04d8afd6be4d7d2b9127:X 22 Aug 23:27:51.003 # +monitor master mymaster 192.168.92.136 6379 quorum 19127:X 22 Aug 23:27:51.004 * +slave slave 192.168.98.136:6380 192.168.92.136 6380 @ mymaster 192.168.98.136 63799127:X 22 Aug 23:27:51.005 * +slave slave 192.168.98.136:6381 192.168.92.136 6381 @ mymaster 192.168.98.136 63799127:X 22 Aug 23:27:59.017 * +sentinel sentinel 73d21ad0584a4ce628c9aa5cc0653eda169b9080 192.168.98.136 26380 @ mymaster 192.168.98.136 63799127:X 22 Aug 23:28:18.366 # +new-epoch 19127:X 22 Aug 23:28:18.367 # +vote-for-leader 73d21ad0584a4ce628c9aa5cc0653eda169b9080 19127:X 22 Aug 23:28:18.417 # +sdown master mymaster 192.168.98.136 63799127:X 22 Aug 23:28:18.417 # +odown master mymaster 192.168.98.136 6379 #quorum 1/19127:X 22 Aug 23:28:18.417 # Next failover delay: I will not start a failover before Mon Aug 22 23:30:58 20169127:X 22 Aug 23:28:19.465 # +config-update-from sentinel 73d21ad0584a4ce628c9aa5cc0653eda169b9080 192.168.98.136 26380 @ mymaster 192.168.98.136 63799127:X 22 Aug 23:28:19.465 # +switch-master mymaster 192.168.92.136 6379 192.168.92.136 63819127:X 22 Aug 23:28:19.465 * +slave slave 192.168.92.136:6380 192.168.98.136 6380 @ mymaster 192.168.98.136 63819127:X 22 Aug 23:28:19.465 * +slave slave 192.168.92.136:6379 192.168.98.136 6379 @ mymaster 192.168.98.136 6381
说明:
+sdown表示哨兵主观认为master已经停止服务了;+odown表示哨兵客观认为master停止服务了。接着哨兵开始进行故障恢复,挑选一个slave升级为master;+try-failover表示哨兵开始进行故障恢复;+failover-end 表示哨兵完成故障恢复;+slave表示列出新的master和slave服务器,我们仍然可以看到已经停掉的master,哨兵并没有清除已停止的服务的实例,这是因为已经停止的服务器有可能会在某个时间进行恢复,恢复以后会以slave角色加入到整个集群中;