分布式缓存技术Redis(四)分布式与集群

一、主从复制

主从复制集群的目的是把Redis的数据库复制多个副本部署在不同服务器上,如果其中一台服务器出现故障,也能听快速迁移到其他服务器上提供服务。主从复制功能可以实现主Redis服务器的数据更新后,自动将更新的数据同步到其他从节点服务器。而一般情况下从节点只读,并接受主节点同步过来的数据,一个主节点可以有多个从节点。

分布式缓存技术Redis(四)分布式与集群_第1张图片

配置

master/slave模式配置只需要将从节点加入slaveof主节点的地址,而master节点不需要做任何改变。

  1. 在slave节点的redis.conf文件增加slaveof [主节点IP] 6379、同时注释bindIp这行配置允许所有ip访问。
  2. 启动从节点Redis服务。
  3. 访问从节点服务的Redis客户端,输入INFO replication
  4. 在主服务客户端任意添加一个key,此时就可以在从节点中看到有数据同步过来。

原理

  1. 全量复制

Redis全量复制一般发生在Slave初始化阶段,这时Slave会将Master上的所有数据复制一份。

分布式缓存技术Redis(四)分布式与集群_第2张图片

完成以上步骤就完成了slave节点数据初始化的所有操作,slave节点此时可以接收来自用户的读请求。

master/slave复制策略采用乐观复制,可以容忍在一段时间内主节点和从节点的数据内容不同,但是两者的数据会最终同步。Redis的主从同步过程是异步的,意味着master执行完客户端请求的命令后会立即返回结果给客户端,然后再异步的方式将命令同步给slave。这一特征保证了master的性能不受slave影响。

如果在数据不一致的窗口期间,master/slave因为网络问题断开连接。这个时候master无法得知某个命令最终同步给了多少个slave。不过Redis在redis.conf文件提供了一个配置项来限制只有数据至少同步给多少个slave的时候,master才可写。

#表示只有当3个以上的slave连接到master,master才可写
min-slaves-to-write 3
#表示允许slave最长失去连接的时间,如果10秒还没有收到这个slave的响应,则master认为这个slave已经断开
min-slaves-max-lag 10
  1. 增量复制

从Redis2.8开始,就支持主从复制的断点续传,如果主从复制过程中,网络断开连接,那么可以接着上次复制的地方继续往下复制,而不是从头开始复制。

master节点会在内存中创建一个backlog,master和slave都会保存一个replica offset和一个master id,offset保存在backlog中。如果master和slave断开网络连接,slave会让master从上次的replica offset开始继续复制,如果没有找到对应的offset就执行全量复制。

  1. 无硬盘复制

Redis的复制是基于RDB快照的方式持久化实现的,也就是master在后台保存RDB快照,slave接收到RDB文件并载入,但这种方式存在以下问题:

  1. 当master禁用RDB时,如果执行了复制初始化操作,Redis依然会生成RDB快照,当master下次启动执行RDB的恢复时,因为复制发生的时间点不确定,所以恢复的数据可能是任意时间节点的,就会数据出现问题。
  2. 当硬盘性能比较慢的情况下,初始化复制过程会对性能产生影响。

因此Redis2.8.18以后的版本引入了无硬盘复制选项,同步时master在内存中创建RDB不需要生成在本地硬盘中,从而直接发送数据,在redis.conf中修改以下配置开启功能。

repl-diskless-sync yes

二、哨兵机制(sentsentinel)

master/slave模式是一个典型的一主多从模式,slave在系统中起到了数据冗余备份和读写分离的作用。但是当master遇到异常终端后,需要从slave中选举一个新的master继续对外提供服务。Redis并没有提供自动master的选举功能,而是需要借助一个哨兵来监控Redis系统的运行状态。它的功能包括以下两个:

  1. 监控master和slave是否正常运行。
  2. master出现故障时将slave升级成为master。

分布式缓存技术Redis(四)分布式与集群_第3张图片
哨兵集群

上图中虽然master选举得到了解决,但是此时哨兵是单节点的,也会引发哨兵的单点故障。所以可以使用多个哨兵进行监控任务以保证系统足够稳定。此时哨兵不仅会监控master和slave,同时还会相互监控。这种方式称为哨兵集群,哨兵集群需要解决故障发现和master决策的协商机制问题。

分布式缓存技术Redis(四)分布式与集群_第4张图片

sentsentinel节点之间会因为共同监控同一个master产生关联,一个新加入的sentinel节点需要和其他监控master节点的sentinel相互感知。

  1. 需要互相感知的sentinel都向他们功能监控的master节点订阅一个channel。
  2. 新加入的sentinel节点向这个channel发布一个消息,包含自己本身的信息,这样订阅了这个channel的sentinel就可以发现新加入的sentinel。
  3. 新加入的sentinel和其他sentinel节点建立长连接。
master故障发现

sentinel节点会定期向master节点发送心跳包来判断存活状态,一旦master节点没有正确响应,sentinel就会把master设置为"主观不可用状态",然后它会把"主观不可用状态"发送给其他sentinel节点确认,当确认的sentinel节点数大于quorum(一般指半数以上)时,则会认为master是"客观不可用",接着就开始进入选举新的master流程。但是这里是一个sentinel集群,如果多个sentinel发现master节点达到"客观不可用"不清楚由哪一个sentinel来做决策。所以要从sentinel集群中选择一个leader来做决策。这里用到了基于投票半数通过提议的Raft分布式一致性算法。

Raft算法演示:http://thesecretlivesofdata.com/raft/

配置实现
在任意一台服务器上创建一个`sentinel.conf`文件
port 6040

#语法:sentinel monitor [name] [ip] [port]  [quorum]
#解释:name表示master的名字,名字自定义;ip和port表示master节点的ip和端口。quorum表示最低通过票数,通过这个票数sentinel节点才可以达到统一决策。
sentinel monitor myMaster 192.168.8.11 6379 1

#表示5秒内没有响应,就认为sdown
sentinel down-after-millseconds myMaster 5000 

#表示15秒master没有活过来,就认为fialover,从存活的slave中重新选举master
sentinel failover-timeout myMaster 15000

启动哨兵

#方式一
./redis-sentinel [文件路径]/sentinel.conf

#方式二
./redis-server [文件路径]/sentinel.conf --sentinel

哨兵监控Redis主从模式时,只需要配置监控master即可,哨兵会自动发现所有slave。sentinel有如下输出消息:

  1. +sdown
    表示哨兵主观认为master出现故障。
  2. +odown
    表示哨兵客观认为master故障。随后哨兵开始进行故障恢复,从slave中重新选举master。
  3. +try-failover
    表示哨兵开始进行故障恢复。
  4. +failover-end
    表示哨兵完成故障恢复。
  5. +slave
    表示Redis加入新的slave节点。

三、Redis Cluster

在主从复制模式中,就算多了从节点,每个节点仍然存在整个Redis的所有数据,从而导致集群的总数据存储量受限于可用内存最小的节点,所以这个问题仍需要解决。

在Redis3.0之前是通过Redis客户端去做分片,通过hash环的方式对key进行分片存储。分片虽然能够解决单个节点的存储压力,但是导致维护成本高、增加或移除节点比较繁琐。因此在Redis3.0之后的版本最大的一个好处就是支持集群功能。集群的特点在于拥有和单击实例一样的性能,同时在网络分区以后能够提供一定的可访问性以及对主数据库故障恢复的支持。哨兵和集群是两个独立的功能,当不需要对数据进行分片处理使用哨兵机制就足够,如果需要进行水平扩容,集群是一个更好的方式。

拓扑结构

一个Redis Cluster由多个Redis节点构成。不同节点组成服务的数据没有交集,也就是每一个节点组对应数据sharding的一个分区。节点组内部分为主从两类节点,对应master节点和slave节点。两者数据一致,通过异步的主从复制机制来保证。一个节点组有且只有一个master节点,同时可以有0到多个slave节点,同样只有master节点对客户端提供读写服务,slave提供备份和读服务。

Redis Cluster是基于gossIp协议实现的去中心化节点的集群,因为去中心化的结构不存在统一的配置中心,各个节点对整个集群状态的认知来自节点之间的信息交互。在Redis Cluster中这个信息交互通过Redis Cluster Bus来完成。Redis Cluster Bus通过单独的端口进行连接,Bus是节点间的内部通信机制,采用字节序列化信息交互。

数据分区

分布式数据库首要的是把整个数据集按照分区规则映射到多个节点的问题,即把数据集划分到多个节点上,每个节点负责整个数据的一个子集。Redis Cluster采用哈希分区规则虚拟槽分区。

虚拟槽分区巧妙的使用了哈希空间,使用分散度良好的哈希函数把所有的数据映射到一个固定范围内的整数集合,整数定义为槽(slot)。比如Redis Cluster槽的范围是0~16383。槽是集群内数据管理和迁的基本单位。采用大范围的槽的主要目的是为了方便数据的拆分和集群的扩展,每个节点负责一定数量的槽。slot = CRC(key)%16384,每个节点负责一部分槽。

分布式缓存技术Redis(四)分布式与集群_第5张图片

HashTag

对上述Redis Cluster模式会存在一个矛盾点,要求key尽可能的分散在不同机器,又要求某些相关联的key分配到相同机器。因为分片其实就是一个hash的过程,对key做hash取模然后划分到不同的机器上。所以为了解决这个问题,我们需要考虑如何让相关联的key得到的hash值都相同。如果key全部相同是不现实的,所以要在redis中引入了HashTag的概念,可以使得数据分布算法可以根据key的某一个部分进行计算,然后让相关的key落到同一个数据分片。

例如对用户信息存储,可以这样设置key:user:{user1}:id 和 user:{user1}:name
通过hashTag的方式这个两个Key就分配到一个hash槽中,因为当一个key包含{}的时候,就不对整个key做hash,而仅对{}里的内容做hash。

重定向

Redis Cluster并不会代理查询,如果客户端访问了一个key并不存在的节点。就会返回客户端下面信息:-MOVED [槽点] [key所在的ip:端口号]
如果根据key计算得出的槽恰好由当前节点负责,则当前节点会立即返回结果。
但在我们使用Redis的java客户端时不必担心这个问题,因为客户端内部已经做了重定向到key所在的分片。

分片迁移

在一个稳定的Redis Cluster下,每一个slot对应的节点是确定的,但是在某些情况下,节点和分片关系会发生变更。也就是说当动态添加或减少node节点时,需要将16384个槽做个再分配,槽中的键值也要迁移:

  1. 新加入master节点。
  2. 某个节点宕机。

新增一个主节点

Redis Cluster的做法是从各个节点的前面各取一部分slot分配到新的分片节点上。

删除一个主节点

先将节点的数据迁移到其他节点上,然后才执行删除。

槽迁移的过程

槽迁移的过程是一个不稳定状态,这个不稳定状态会有一些规则,这些规则定义客户端的行为,从而使得Redis Cluster 不必宕机的情况下可以执行槽的迁移。

分布式缓存技术Redis(四)分布式与集群_第6张图片

上图迁移流程

  1. 向master B发送状态更改命令,把master B对应的迁出slot设置为IMPORTING状态。
  2. 向master A发送状态更改命令,将master A对应的迁入slot设置为MIGRANTING状态。

IMPORTING状态当来自客户端的正常访问不是从ASK跳转过来的,说明客户端还不知道迁移正在进行,很有可能操作了一个目前还没迁移完成的并且还存在于masterA上的key,如果此时这个key在A上已经被修改了,那么B和A的修改则会发生冲突。所以对于masterB上的迁入slot上的所有非ASK跳转过来的操作,masterB都不会去处理,而是通过MOVED命令让客户端跳转到masterA上去执行。

你可能感兴趣的:(分布式缓存技术,redis)