数据复制
Redis默认使用的是异步复制,其特点低延迟和高性能,是绝大多数Redis用例的自然复制模式。从Redis服务器会异步的确认其从主服务器周期接收到的数据量
客户端可以使用WAIT命令来请求同步复制特定的数据。
Redis复制的非常重要的事实
- Redis使用异步复制,slave和master之间异步的确认处理的数据量
- 一个master可以有多个slave
- slave可以接受其它salve的链接。除了多个slave可以连接到同一个master外,slave之间也可以像层叠状的结构连接到其它slave。
- Redis复制在master是非阻塞的。这意味着master在一个或多个slave进行初次同步或者部分重同步时,可以继续处理查询请求
- 复制在slave大部分也是非阻塞的。当slave进行初次同步时,它可以使用旧数据集处理查询请求,需要在redis.conf中配置。也可以配置如果复制流断开,Redis slave会返回一个error给客户端。但是在初次同步之后,旧的数据集必须被删除,同时加载新的数据集
- 复制既可以被用在可伸缩性,以便只读查询可以有多个slave进行,有课用于数据安全
- 可以使用复制来避免master将全部数据集写入磁盘造成的开销:一种典型的技术时配置你的master Redis.conf以避免对磁盘进行持久化,然后连接一个slave,其配置为不定期保存或启用AOF。注意:这个设置需要小心处理,因为重新启动master程序将从一个空数据集开始:如果一个slave视图与它同步,那么这个slave也会被清空
master关闭持久化时,复制的安全性
在使用Redis复制时的设置中,在master和slave中启用持久化。当不可能启用时,例如由于非常慢的磁盘性能而导致的延迟问题,应该配置实例来避免重置后自动重启
当Redis Sentinel被用于高可用并且master关闭持久化,这时如果允许自动重启进程也是危险的。任何时候数据安全性都是很重要的,所以如果master使用复制功能的同时未配置持久化,那么自动重启进程这项应该被禁用
Redis复制功能工作原理
每一个Redis master都有一个replication ID:这是一个较大的伪随机字符串,标记了一个给定的数据集。每个master也持有一个偏移量,master将自己产生的复制流发送给slave时,发送多少个字节的数据,自身的偏移量就会增加多少,目的时当有新的操作修改自己的数据集时,它可以以此更新slave的状态。复制偏移量即使在没有一个slave连接到master时,也会自增,所以基本上每一对给定的 Replication ID, offset, 都会标识一个master数据集的确切版本
当slave连接到master时,它们使用PSYNC命令来发送他们记录的旧的master replication ID 和它们至今为止的偏移量。通过这种方式,master能够发送slave所需的增量部分。但是如果master的缓冲去中没有足够的命令积压缓冲记录,或者如果slave引用了不再知道的历史记录(replication ID),则会转而进行一个全量重同步:在这种情况下,slave会得到一个完整的数据集副本,从头开始
只读slave
slave支持只读模式且默认开启。redis.conf文件中的slave-read-only 变量控制的这个行为,且可以在运行时使用CONF SET来随时开启或者关闭
允许只写入N个附件的副本
只有当至少有N个slave连接到master时,才有可能配置Redis master接受写查询。
但是由于Redis使用异步复制,因此无法确保slave是否实际接收到给定的写命令,因此总会有一个数据丢失窗口。
工作原理:
- Redis slave每秒都会ping master,确认已处理的复制流数量
- Redis master会记录上一次从每个slave都收到ping的时间
- 用户可以配置一个最小的slave数量,使得它滞后 <= 最大秒数
- 这个特性有两个配置参数:
- min-slaves-to-write <slave数量>
- min-slaves-max-lag <秒数>
Redis复制如何处理key的过期
Redis的过期机制可以限制key的生存时间。此功能取决于Redis实列计算时间的能力,但是即使使用Lua脚本更改了这些key,Redis slaves也正确地复制具有过期时间的key。
为了实现这样的功能,Redis不能依靠主从使用同步时钟,因为这是一个无法解决的并且会导致 race condition和数据集不一致的问题,所以Redis使用三种主要的技术使过期的key的复制能够正确工作:
- slave 不会让key过期,而是等待master让key过期。当一个master让一个key到期时,它会合成一个DEL命令并且传输到所有的slave
- 但是由于这是master驱动的key过期行为,master无法及时提供DEL命令,所以有时候slave的内存中仍然可能存在在逻辑上已经过期的key。为了处理这个问题,slave使用它的逻辑时钟以报告只有再不违反数据集的一致性读取操作中存在key。用这种方法,slave避免报告逻辑过期的key仍然存在。
- 在Lua脚本执行期间,不执行任何 key 过期操作。当一个Lua脚本运行时,从概念上讲,master 中的时间是被冻结的,这样脚本运行的时候,一个给定的键要么存在要么不存在。这可以防止 key 在脚本中间过期,保证将相同的脚本发送到 slave ,从而在二者的数据集中产生相同的效果
重新启动和故障转移后的部分重同步
当一个实例在故障转移后被提升为 master 时,它仍然能够与旧 master 的 slaves 进行部分重同步。为此,slave 会记住旧 master 的旧 replication ID 和复制偏移量,因此即使询问旧的 replication ID,其也可以将部分复制缓冲提供给连接的 slave 。
但是,升级的 slave 的新 replication ID 将不同,因为它构成了数据集的不同历史记录。例如,master 可以返回可用,并且可以在一段时间内继续接受写入命令,因此在被提升的 slave 中使用相同的 replication ID 将违反一对复制标识和偏移对只能标识单一数据集的规则。
另外,slave 在关机并重新启动后,能够在 RDB 文件中存储所需信息,以便与 master 进行重同步。这在升级的情况下很有用。当需要时,最好使用 SHUTDOWN 命令来执行 slave 的保存和退出操作。