一、Redis的集群方案
主从复制集群:手动切换的主从复制集群、带有哨兵的HA的主从复制集群。
分片集群:客户端实现路由索引的分片集群、使用中间件代理层的分片集群、Redis自身实现的Cluster分片集群。
二、Redis主从复制的原理
1、主从复制机制
当一个master实例和一个slave实例连接正常时, master 会发送一连串的命令流来保持对slave的更新,以便于将自身数据集的改变复制给 slave , 包括客户端的写入、key的过期或被逐出等等。
当master和slave之间的连接断开之后,因为网络问题、或者是主从意识到连接超时, slave重新连接上master会尝试进行部分同步,这意味着它会尝试只获取在断开连接期间内丢失的命令流。
当无法进行部分重同步时, slave会请求进行全量重同步。这会涉及到一个更复杂的过程,例如master需要创建所有数据的快照,将之发送给 slave ,之后在数据集更改时持续发送命令流到slave。
2、主从复制的关注点
Redis使用异步复制:slave和master之间异步地确认处理的数据量。
一个master可以拥有多个slave,slave可以接受其他slave的连接。除了多个slave可以连接到同一个master之外,slave之间也可以像层叠状的结构(cascading-likestructure)连接到其他slave。自Redis4.0起,所有的sub-slave将会从master收到完全一样的复制流。
Redis复制在master侧是非阻塞的。这意味着master在一个或多个slave进行初次同步或者是部分重同步时,可以继续处理查询请求。
Redis复制在slave侧大部分也是非阻塞的。当slave进行初次同步时,它可以使用旧数据集处理查询请求,假设你在redis.conf中配置了让Redis这样做的话。在初次同步之后,旧数据集必须被删除,同时加载新的数据集。此外,你也可以配置如果复制流断开,Redis的slave会返回一个error给客户端。slave在这个短暂的时间窗口内(如果数据集很大,会持续较长时间),会阻塞到来的连接请求。自Redis4.0开始,可以配置Redis使删除旧数据集的操作在另一个不同的线程中进行,但是,加载新数据集的操作依然需要在主线程中进行并且会阻塞slave。
三、主从复制的具体过程
每一个Redis的master都有一个replicationID,这是一个较大的伪随机字符串,标记了一个给定的数据集。每个master也持有一个偏移量offset,master将自己产生的复制流发送给slave时,发送多少个字节的数据,自身的偏移量就会增加多少,目的是当有新的操作修改自己的数据集时,它可以以此更新slave的状态。偏移量offset即使在没有一个slave连接到master时,也会自增,所以基本上每一对给定的ReplicationID,offset都会标识一个master数据集的确切版本。
当slave连接到master时,它们使用PSYNC命令来发送它们记录的旧的master节点的replicationID和它们至今为止处理的偏移量。通过这种方式,master能够仅发送slave所需的增量部分。但是如果master的缓冲区中没有足够的命令积压缓冲记录,或者如果slave引用了不再知道的历史记录(replicationID),则会转而进行一个全量重同步:在这种情况下,slave会得到一个完整的数据集副本,从头开始。
下面是一个全量同步的工作细节:
master开启一个后台保存进程,以便于生产一个RDB文件。同时它开始缓冲所有从客户端接收到的新的写入命令。当后台保存完成时,master将数据集文件传输给slave,slave将之保存在磁盘上,然后加载文件到内存。再然后master会发送所有缓冲的命令发给slave。这个过程以指令流的形式完成并且和Redis协议本身的格式相同。
你可以用telnet自己进行尝试。在服务器正在做一些工作的同时连接到Redis端口并发出SYNC命令。你将会看到一个批量传输,并且之后每一个master接收到的命令都将在telnet回话中被重新发出。事实上SYNC是一个旧协议,在新的Redis实例中已经不再被使用,但是其仍然向后兼容:但它不允许部分重同步,所以现在PSYNC被用来替代SYNC。
之前说过,当主从之间的连接因为一些原因崩溃之后,slave能够自动重连。如果master收到了多个slave要求同步的请求,它会执行一个单独的后台保存,以便于为多个slave服务。
补充-无需磁盘参与的复制:正常情况下,一个全量重同步要求在磁盘上创建一个RDB文件,然后将它从磁盘加载进内存,然后slave以此进行数据同步。如果磁盘性能很低的话,这对master是一个压力很大的操作。Redis2.8.18是第一个支持无磁盘复制的版本。在此设置中,子进程直接发送RDB文件给slave,无需使用磁盘作为中间储存介质。