Redis无论是单实例还是集群,都可以启用复制(Replication)。Redis的复制模型支持主从复制(master-slave)以及从从复制,也就是说一个副本可以连接到master上也可以连接到其他副本上。
复制既可以用来实现可拓展性(scalability)例如增加副本为了只读查询,也可以用来增强数据安全性以及高可用性。需要注意的是,从Redis 2.6开始Redis的副本默认支持read-only模式,read-only副本会拒绝所有的写命令,但是这个设计初衷并不是用来将副本暴漏到公网上实现读写分离的,因为一些管理员命令例如DEBUG
或者CONFIG
在read-only模式下都是可用的。所以如果想要实现读写分离,还是要参考一些解决方案。
Redis的复制不会阻塞master
,这意味着master在进行复制的同时依然可以对外提供服务。副本端也基本不会因为复制而阻塞,
复制机制
- 当master与副本实例完成连接,master将通过将命令流(stream of commands)发送到副本端的方式,更新副本。使其同步master的,客户端写操作,key过期及驱逐和其他改变master数据集的操作。
- 当master与副本连接断开,副本会尝试重连master,并执行部分重同步(partial resynchronization):即只找回断联期间丢失部分的命令流。
- 当增量同步不成功,会触发完整重同步(full resynchronization)。
Redis默认使用异步方式进行复制,但是master不会等待每一个命令都被副本执行才继续发送下一个命令,副本是按周期对收到的数据进行确认(acknowledge )的。
如果master需要保证某些重要的操作必须被准备的复制到副本上,那么可以使用WAIT
命令进行阻塞的同步复制:
1 | > wait 1 0 |
WAIT
提供两个参数,第一个参数是从库的数量 N,第二个参数是时间 t,以毫秒为单位。它表示等待WAIT
指令之前的所有写操作同步到 N 个从库 (也就是确保 N 个从库的同步没有滞后),最多等待时间 t。如果时间 t=0,表示无限等待直到 N 个从库同步完成达成一致。
假设此时出现了网络分区,WAIT
指令第二个参数时间 t=0,主从同步无法继续进行,WAIT
指令会永远阻塞,Redis 服务器将丧失可用性。
由于Redis的master是异步复制数据到副本中的,同时副本也是异步进行acknowledge
的,所以复制都不会阻塞master实例和副本实例对外提供服务。但是,当初始同步完成后,需要删除旧的数据集和加载新的数据集,在这个短暂的时间内,副本实例会阻塞连接进来的请求。
复制工作原理
每一个master实例都有一个replication ID,它是一个很大的伪随机数。同时每个master还会维护一个偏移量offset
,表示哪些同步流已经发送给了副本实例。即使没有副本,master也会维护这个offset。因此<replication ID , offset>的二元组就可以标识数据集的一个具体版本。
当副本连接到master(包括第一次或者重连的情况),它会发送PSYNC
命令将它持有的老的master replication ID和它们已经处理后的offset
发送给master。这样根据offset
,master可以只发送增量的部分。如果master实例的buffer空间不足或者master发现传过来的replication ID是旧的,那么就会触发全量同步(full synchronization)。
全量同步(full synchronization)的过程是:master执行bgSave
生成RDB文件,于此同时master会缓存在此期间新执行的写请求。生成RDB文件后,master将RDB文件传输给副本,副本将其保存在硬盘上,然后加载进内存。随后,master将缓存的命令在发送给副本。
关于PSYNC
在Redis 2.8以前,副本的同步命令是SYNC
。PSYNC
与SYNC
的主要区别就在于PSYNC
支持部分重同步(partial resynchronization)。什么是部分重同步呢?
每一个实例,都有两个replication IDs:main ID和 secondary ID。当一个节点启动称为master的时候(无论是从0开始,还是被晋升的),都会生成一个新的replication ID。当副本连接到这个master上的时候,都会继承这个replication ID。当一个副本晋升为master的时候,既会生成新的replication ID,也会记住原有的replication ID(从老的master那里继承来的),这样当副本连接到这个新的master上的时候,传过来的依然是旧的replication ID,新的master可以根据这个旧的replication ID只重新同步差异的数据而不是全部都重新同步。
无盘复制
从2.8.18开始,Redis支持无盘复制,原有的全量同步生成RDB文件需要储存到硬盘上,这是一个很重的IO操作。特别是当系统正在进行AOF的fsync操作时如果发生全量同步,fsync将会被推迟执行,这就会严重影响master的服务效率。
所谓无盘复制是指master直接通过套接字将RDB内容发送到副本,生成RDB内容是一个遍历的过程,master会一边遍历内存,一边将序列化的内容发送到副本上。
Sentinel
有了主从复制机制还不够,当某个节点发生错误时,我们需要有一套自动切换的机制进行主备切换,这就是Sentinel,Sentinel才能为Redis提供真正的高可用。Sentinel本身也是一个分布式系统,使用raft协议保证Sentinel集群的一致性。
Sentinel可以看作是Redis客户端的服务发现,它能够自动做主备切换。
在非集群的部署情况下,一般都是使用主从复制+Sentinel的方式。