虽然redis有持久化功能能够保障redis服务器宕机也能恢复并且只有少量的数据损失,但是由于所有数据在一台服务器上,如果这台服务器出现硬盘故障,那就算是有备份也仍然不可避免数据丢失的问题。在实际生产环境中,我们不可能只使用一台redis服务器作为我们的缓存服务器,必须要多台实现集群,避免出现单点故障;下面来看下redis的集群策略
主从复制
复制的作用是把redis的数据库复制多个副本部署在不同的服务器上,如果其中一台服务器出现故障,也能快速迁移到其他服务器上提供服务。 复制功能可以实现当一台redis服务器的数据更新后,自动将新的数据同步到其他服务器上
主从复制就是我们常见的master/slave模式, 主数据库可以进行读写操作,当写操作导致数据发生变化时会自动将数据同步给从数据库。而一般情况下,从数据库是只读的,并接收主数据库同步过来的数据。 一个主数据库可以有多个从数据库。
主从模式的配置
- 将两台服务器上的redis.conf中 bind 127.0.0.1 注释掉
- 在slave节点上配置 slaveof 192.168.44.131 6379 maste的ip和端口
配置完成启动master,slave节点。redis-cli 连接到salve节点 info replication发现master状态为down
通过查看slave的日志发现如下问题:
Error reply to PING from master: ‘-DENIED Redis is running in protected mode because protected mode is enabled, no bind address was specified, no authentication password is requested to clients.
节点默认情况下是受保护模式运行的,解决方法很简单,给主机配置密码或者 设置为no
原理
全量复制
全量复制一般发生在,slave节点的初始化的时,具体流程如下图所示:
slave完成上述初始化步骤后才可以,对完提供读服务
redis的全量复制是乐观的全量复制,就是说允许在一段时间内master/salve的节点的数据是不同的,单最总会是一致的。redis的主从同步是异步的,redis端执行完客户端的命令后,立即返回,异步的方式将命令通知给master。保证了加入salve后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 节点会在内存中创建一个backlog,master和slave都会保存一个replica offset还有一个master id,offset就是保存在backlog中的。如果master和slave网络连接断掉了,slave会让master从上次的replica offset开始继续复制,但是如果没有找到对应的offset,那么就会执行一次全量同步
无硬盘复制
前面我们说过,Redis复制的工作原理基于RDB方式的持久化实现的,也就是master在后台保存RDB快照,slave接收到rdb文件并载入,但是这种方式会存在一些问题
- 当master禁用RDB时,如果执行了复制初始化操作,Redis依然会生成RDB快照,当master下次启动时执行该RDB文件的恢复,但是因为复制发生的时间点不确定,所以恢复的数据可能是任何时间点的。就会造成数据出现问题
- 当硬盘性能比较慢的情况下(网络硬盘),那初始化复制过程会对性能产生影响
因此2.8.18以后的版本,Redis引入了无硬盘复制选项,可以不需要通过RDB文件去同步,直接发送数据,通过以下配置来开启该功能
repl-diskless-sync yes
master节点在内存中直接创建rdb,然后发送给slave,不会在自己本地落地磁盘了
哨兵机制
master/slave模式,在一个典型的一主多从的系统中,slave在整个体系中起到了数据冗余备份和读写
分离的作用。当master遇到异常终端后,需要从slave中选举一个新的master继续对外提供服务。redis中可借助哨兵来实现
哨兵的作用:
- 监控master和slave是否正常运行
- master出现故障时自动将slave数据库升级为master
在一个一主多从的Redis系统中,可以使用多个哨兵进行监控任务以保证系统足够稳定。此时哨兵不仅会监控master和slave,同时还会互相监控;这种方式称为哨兵集群,哨兵集群需要解决故障发现、和master决策的协商机制问题
sentinel之间可以相互感知,sentinel节点之间会因为共同监听了同一个master而产生关联,一个新加入的节点需要先和其他节点互相感知。
- 每个sentnel向监控的master节点订阅一个channel,比如 sentinel_channel。
- 新加入的sentnel,会向这个channel发送一条消息,包含自身的一些信息,这样其他的sentnel就可以发现这个新加入的sentnel
- 新加入的sentnel和原有的sentnel建立长连接
master的故障发现
sentinel会定期向master节点发送心跳包,来检测master的状态,一旦master未正常响应,sentinel会把master节点设置为主观不可用状态。并把主观不可以能,发送给其他sentinel去确认,当超过一定数量的sentinel确认后(配置哨兵时,配置的),就会被设置成客观不可用。接着就开始选举master的流程。
sentinel的leader选举
当多个sentinel同事发现,master宕机,那么由哪个sentinel决定选取哪个节点为master呢?需要一个leader来做决策。
sentinel的leader选举采用Raft算法(分布式一致性算法)。
动画演示地址:http://thesecretlivesofdata.com/raft/
sentinel配置
编辑sentinel.conf文件 添加:sentinel monitor name ip port quorum
其中name表示要监控的master的名字,这个名字是自己定义。 ip和port表示master的ip和端口号。 最后一个1表示最低通过票数,也就是说至少需要几个哨兵节点统一才可以
port 6040
sentinel monitor mymaster 192.168.44.130 6379 1
sentinel down-after-milliseconds mymaster 5000 --表示如果5s内mymaster没响应,就认为SDOWN
sentinel failover-timeout mymaster 15000 --表示如果15秒后,mysater仍没活过来,则启动failover,从剩下的slave中选一个升级为master
两种方式启动哨兵
redis-sentinel sentinel.conf
redis-server /path/to/sentinel.conf --sentinel哨兵监控一个系统时,只需要配置监控master即可,哨兵会自动发现所有slave;
这时候,我们把master关闭,等待指定时间后(默认是30秒),会自动进行切换,会输出如下消息
+sdown 表示哨兵主管认为master已经停止服务了,+odown表示哨兵客观认为master停止服务了。接着哨兵开始进行故障恢复,挑选一个slave升级为master
+try-failover 表示哨兵开始进行故障恢复
+failover-end 表示哨兵完成故障恢复
+slave 表示列出新的master和slave服务器,我们仍然可以看到已经停掉的master,哨兵并没有清楚已停止的服务的实例,这是因为已经停止的服务器有可能会在某个时间进行恢复,恢复以后会以slave角色加入到整个集群中
Redis-Cluster
一个Redis Cluster由多个Redis节点构成。不同节点组服务的数据没有交集,也就是每个一节点组对应数据的一个分片。节点组内部分为主备两类节点,对应master和slave节点。两者数据准实时一致,通过异步化的主备复制机制来保证。一个节点组有且只有一个master节点,同时可以有0到多个slave节点,在这个节点组中只有master节点对用户提供些服务,读服务可以由master或者slave提供
数据分区
分布式数据库首要解决把整个数据集按照分区规则映射到多个节点的问题,即把数据集划分到多个节点上,每个节
点负责整个数据的一个子集, Redis Cluster采用哈希分区规则,采用虚拟槽分区。
虚拟槽分区巧妙地使用了哈希空间,使用分散度良好的哈希函数把所有的数据映射到一个固定范围内的整数集合,
整数定义为槽(slot)。比如Redis Cluster槽的范围是0 ~ 16383。槽是集群内数据管理和迁移的基本单位。采用
大范围的槽的主要目的是为了方便数据的拆分和集群的扩展,每个节点负责一定数量的槽。
计算公式:slot = CRC16(key)%16383。每一个节点负责维护一部分槽以及槽所映射的键值数据。
hashTag
通过分片手段,可以将数据合理的划分到不同的节点上。但是有时我们有希望有业务关联的key,划分到相同节点。
在redis中引入了HashTag的概念,可以使得数据分布算法可以根据key的某一个部分进行计算,然后
让相关的key落到同一个数据分片举个简单的例子,加入对于用户的信息进行存储, user:user1:id、user:user1:name 那么通过hashtag的方式,user:{user1}:id、user:{user1}.name; 表示
当一个key包含 {} 的时候,就不对整个key做hash,而仅对 {} 包括的字符串做hash