在redis副本的基础上,redis提供了简单易用的主从模式。从节点会精确的从主节点复制数据,并且在连接中断的时候自动重连,无论主节点发生什么都会尝试复制数据。
实现机制主要如下3种:
Redis默认使用异步的副本,这样延迟低性能高,是绝大多数redis实例的自然复制模式。然而从节点会异步的定期和主节点确认接收到的数据量,所以主节点不会每次等待数据同步给从节点,但是有需要的话,主节点也直到哪个从节点接收了哪些数据,所以也允许使用同步的副本。
客户端可以使用WAIT命令请求某些数据的同步复制。然而,wait命令只能确保其他Redis实例中存在指定数量的已确认copies,它不会将一组Redis实例转换为具有强一致性的CP系统:根据Redis持久化的配置,在故障切换期间仍可能丢失已确认的写入。然而,使用wait命令,写入丢失的情况会大大减小,只局限于某些难以出发的故障中。
当使用redis主从时,强烈建议主节点和从节点都开启持久化。如果没办法的话(比如磁盘很慢会造成高延迟),一定要避免redis程序自动重启。因为如果主节点所在机器崩溃了,在重启后redis服务也自动重启了,那么所有从节点会自动从主节点复制数据。这会导致所有从节点中的数据也丢失了。
当使用哨兵做高可用时,关闭master的持久化和开启自动重启是很危险的。因为重启过程可能很快,哨兵检测不到主节点故障。
每个master都有一个replication id:一个长的伪随机字符串,用来表示数据集。同时master还使用随复制流字节递增的offset,来完成从节点的数据更新。就算实际上没有可连接的从节点,offset还是会递增,所以下面的信息可以master数据集的精确版本:
Replication ID, offset
当有从节点连接master时,master使用psync命令来发送旧的replication id和目前位置的offset。这样可以只发送所需的增量部分。然而如果master缓冲区没有足够的数据,或者从节点正在引用一个不再使用的replication id,此时就会执行全量复制,从节点会受到从头开始的数据集的完整副本。
全量同步的工作细节:
master后台启动一个保存程序来生成rdb文件。同时将从客户端接收到的指令放入缓冲区。当rdb生成完成,master将这个文件同步给从节点,从节点将文件落盘,然后加载到内存中。然后master将缓冲区的数据发送给从节点,这个流程使用和redis协议一致格式的指令流来完成。
你可以使用telnet自己尝试一下。连接到redis端口然后使用sync命令,你会看到批量的传输,且master将接收到的每个指令都显示在telnet session上。实际上sycn是一个新版本不再使用的旧协议,但是依然兼容。因为sync不允许部分重新同步,所以现在使用psync。
上面提到,当master的连接断了,replica会自动重连。如果master同时收到多个replica的同步请求,它只生成一个后台程序来完成所有replica的同步。
如果两个redis实例的Replication ID和offset一样,那么数据集也是一模一样的。但是也需要理解一下什么是Replication ID ,为什么一个实例有两个Replication ID :main id和secondary ID。
一个Replication ID大体上就是用来表示数据集的一个历史版本。每次一个实例从零开始作为master启动,或者replica升级为master,这个实例就会生成一个新的Replication ID。当一个replica连接一个master握手完成后,他就会继承master的Replication ID。所以两个Replication ID一样的实例实际上也拥有同一个数据集,但可能在不同的时间。对于一个历史Replication ID来说,offset可以视为逻辑上的时间来查看哪个实例保存了最新的数据。
例如,两个实例A和B的Replication ID一样,但是其中一个offste是1000,另一个是1023,这就意味着A的数据集中缺了某些命令,补上这些命令后就与B一致了。
至于为什么有两个Replication ID,是因为replica可能升级为master。在发生一次故障转移后,新的master依然会保存旧的Replication ID,因为旧的Replication ID代表了上一个master。当其他replica尝试与新master同步时会使用旧的Replication ID。replica成为master时,会将secondary id和main id交换,并记录id切换时的offset。当replica与这个新的master连接时,他会用新的id和secondary id来匹配replica给出的id和offset。简而言之就是故障切换之后,replica不必执行全部重新同步。
你可能疑惑为什么升级为master要使用新的id,因为旧的master可能因为网络分隔的原因仍在运行,这样就违背了一样的id和offset代表一样的数据的原则。
通常全量的重新同步需要将rdb文件落盘并加载到内存中,但是在磁盘很慢的时候,这个操作的开销比较大。Redis2.8.18开始提供了无盘复制,master的子进程会直接将rdb通过线缆直接传给replica,而不需要磁盘这样的中间存储。
要使用replica很简单,只需要在replica的配置文件中添加一行:
replicaof 192.168.1.1 6379
ip和端口替换成你的master的ip和端口即可。或者你可以调用replicaof命令。