Redis主从复制原理总结

Redis主从复制原理总结

和Mysql主从复制的原因一样,Redis虽然读取写入的速度都特别快,但是也会产生读压力特别大的情况。为了分担读压力,Redis支持主从复制,Redis的主从结构可以采用一主多从或者级联结构,Redis主从复制可以根据是否是全量分为全量同步和增量同步

全量同步
Redis全量复制一般发生在Slave初始化阶段,这时Slave需要将Master上的所有数据都复制一份。具体步骤如下:

  • 从服务器连接主服务器,发送SYNC命令;
  • 主服务器接收到SYNC命名后,开始执行BGSAVE命令生成RDB文件并使用缓冲区记录此后执行的所有写命令;
  • 主服务器BGSAVE执行完后,向所有从服务器发送快照文件,并在发送期间继续记录被执行的写命令;
  • 从服务器收到快照文件后丢弃所有旧数据,载入收到的快照;
  • 主服务器快照发送完毕后开始向从服务器发送缓冲区中的写命令;
  • 从服务器完成对快照的载入,开始接收命令请求,并执行来自主服务器缓冲区的写命令(缓冲区的命令执行完之后执行非缓冲命令就是增量同步)

增量同步
Redis增量复制是指Slave初始化后开始正常工作时主服务器发生的写操作同步到从服务器的过程。
增量复制的过程主要是主服务器每执行一个写命令就会向从服务器发送相同的写命令,从服务器接收并执行收到的写命令。

Redis主从同步策略
主从刚刚连接的时候,进行全量同步;全同步结束后,进行增量同步。当然,如果有需要,slave 在任何时候都可以发起全量同步。redis 策略是,无论如何,首先会尝试进行增量同步,如不成功,要求从机进行全量同步。

注意点
如果多个Slave断线了,需要重启的时候,因为只要Slave启动,就会发送sync请求和主机全量同步,当多个同时出现的时候,可能会导致Master IO剧增宕机。

主从复制的一些特点:

1)采用异步复制;
2)一个主redis可以含有多个从redis;
3)每个从redis可以接收来自其他从redis服务器的连接;
4)主从复制对于主redis服务器来说是非阻塞的,这意味着当从服务器在进行主从复制同步过程中,主redis仍然可以处理外界的访问请求;
5)主从复制对于从redis服务器来说也是非阻塞的(但在加载新数据时是阻塞的),这意味着,即使从redis在进行主从复制过程中也可以接受外界的查询请求,只不过这时候从redis返回的是以前老的数据,
如果你不想这样,那么在启动redis时,可以在配置文件中进行设置,那么从redis在复制同步过程中来自外界的查询请求都会返回错误给客户端;(虽然说主从复制过程中
对于从redis是非阻塞的,但是当从redis从主redis同步过来最新的数据后还需要将新数据加载到内存中,在加载到内存的过程中是阻塞的,在这段时间内的请求将会被阻,
但是即使对于大数据集,加载到内存的时间也是比较多的);
6)主从复制提高了redis服务的扩展性,避免单个redis服务器的读写访问压力过大的问题,同时也可以给为数据备份及冗余提供一种解决方案;
7)为了编码主redis服务器写磁盘压力带来的开销,可以配置让主redis不在将数据持久化到磁盘,而是通过连接让一个配置的从redis服务器及时的将相关数据持久化到磁盘,
不Redis大概主从同步是怎么实现的?

Redis大概主从同步是怎么实现的?

全量同步:
1.master服务器会开启一个后台子进程用于将redis中的数据生成一个rdb文件,与此同时,主服务器会缓存所有接收到的来自客户端的写命令(包含增、删、改),
2.当后台保存进程处理完毕后,会将该rdb文件传递给slave服务器,而slave服务器会将rdb文件保存在磁盘并通过读取该文件将数据加载到内存,
3.slave将RDB加载到内存完成后,master服务器会将在此期间缓存的命令通过redis传输协议发送给slave服务器,然后slave服务器将这些命令依次作用于自己本地的数据集上最终达到数据的一致性。

部分同步:
从redis 2.8版本以前,并不支持部分同步,当主从服务器之间的连接断掉之后,master服务器和slave服务器之间都是进行全量数据同步,但是从redis 2.8开
始,即使主从连接中途断掉,也不需要进行全量同步,因为从这个版本开始融入了部分同步的概念。

部分重同步功能由以下三个部分构成:

  • 主服务器的复制偏移量(replication offset)和从服务器的复制偏移量;
  • 主服务器的复制积压缓冲区(replication backlog);
  • 服务器的运行ID(run ID)

1.部分同步的实现依赖于在master服务器内存中给每个slave服务器维护了一份同步日志和同步标识,每个slave服务器在跟master服务器进行同步时都会携带自己的同步标识和上次同步的最后位置(即复制偏移量replication offset),第一次连接时会把主服务的运行ID(master run id)拷贝到从服务保存起来。
2.当主从连接断掉之后,slave服务器隔断时间(默认1s)主动尝试和master服务器进行连接,如果从服务器携带的偏移量标识和主服务运行ID,

  • 1.如果主服务ID相同,即先前的主服务没有重启或者挂掉,并且偏移量还在master服务器上的同步备份日志中(replication backlog),那么就从slave发送的偏移量开始继续上次的同步操作,
  • 2.如果主服务运行ID不同或者slave发送的偏移量已经不再master的同步备份日志中(可能由于主从之间断掉的时间比较长或者在断掉的短暂时间内master服务器接收到大量的写操作),则必须进行一次全量更新。

3.在部分同步过程中,master会将本地记录的同步备份日志中记录的指令依次发送给slave服务器从而达到数据一致。不过这样会存在一个问题,就是主redis服务器一旦重启,因为主redis服务器数据为空,这时候通过主从同步可能导致从redis服务器上的数据也被清空,因此推荐主从都做持久化;

复制积压缓冲区
复制积压缓冲区是由主服务器维护的一个固定长度(fixed-size)先进先出(FIFO)队列,默认大小为1MB。

和普通先进先出队列随着元素的增加和减少而动态调整长度不同,固定长度先进先出队列的长度是固定的,当入队元素的数量大于队列长度时,最先入队的元素会被弹出,而新元素会被放入队列。

当主服务器进行命令传播时,它不仅会将写命令发送给所有从服务器,还会将写命令入队到复制积压缓冲区里面
当从服务器重新连上主服务器时,从服务器会通过PSYNC命令将自己的复制偏移量offset发送给主服务器,主服务器会根据这个复制偏移量来决定对从服务器执行何种同步操作:

如果offset偏移量之后的数据(也即是偏移量offset+1开始的数据)仍然存在于复制积压缓冲区里面,那么主服务器将对从服务器执行部分重同步操作;
相反,如果offset偏移量之后的数据已经不存在于复制积压缓冲区,那么主服务器将对从服务器执行完整重同步操作

服务器运行ID:master run id
除了复制偏移量和复制积压缓冲区之外,实现部分重同步还需要用到服务器运行ID(run ID):

每个Redis服务器,不论主服务器还是从服务,都会有自己的运行ID;
运行ID在服务器启动时自动生成,由40个随机的十六进制字符组成,例如53b9b28df8042fdc9ab5e3fcbbbabff1d5dce2b3;
当从服务器对主服务器进行初次复制时,主服务器会将自己的运行ID传送给从服务器,而从服务器则会将这个运行ID保存起来(注意哦,是从服务器保存了主服务器的ID)。

当从服务器断线并重新连上一个主服务器时,从服务器将向当前连接的主服务器发送之前保存的运行ID:

如果从服务器保存的运行ID和当前连接的主服务器的运行ID相同,那么说明从服务器断线之前复制的就是当前连接的这个主服务器,主服务器可以继续尝试执行部分重同步操作;
相反地,如果从服务器保存的运行ID和当前连接的主服务器的运行ID并不相同,那么说明从服务器断线之前复制的主服务器并不是当前连接的这个主服务器,主服务器将对从服务器执行全量同步操作。

注意
强烈建议在主服务器上开启持久化,当在高可用系统中使用Redis Sentinel,关闭了主服务器的持久化,并且允许自动重启,这种情况是很危险的。

无磁盘复制

通常来讲,一个完全重新同步需要在磁盘上创建一个RDB文件,然后加载这个文件以便为从服务器发送数据。

如果使用比较低速的磁盘,这种操作会给主服务器带来较大的压力。Redis从2.8.18版本开始尝试支持无磁盘的复制。
使用这种设置时,子进程直接将RDB通过网络发送给从服务器,不使用磁盘作为中间存储。

实操配置

主从复制的配置十分简单:
主服务器不需要额外配置,如果主服务加了密码验证,则从服务也需要加,验证方式。

从服务器的配置文件中加入如下配置即可

slaveof 192.168.1.1 6379

当然你需要把其中的192.168.1.1 6379替换为你自己的主服务器IP(或者主机名hostname)和端口。另外你可以调用SLAVEOF命令,
主服务器就会开始与从服务器同步。

关于部分重新同步,还有一些针对复制内存缓冲区的优化参数。查看Redis介质中的Redis.conf示例获得更多信息。

repl-diskless-sync配置参数来启动无磁盘复制。
repl-diskless-sync-delay 参数来配置传输开始的延迟时间,以便等待更多的从服务器连接上来。
查看Redis介质中的Redis.conf示例获得更多信息

从服务器只读模式

从Redis 2.6开始,从服务器支持只读模式,并且是默认模式。这个行为是由Redis.conf文件中的slave-read-only 参数控制的,
可以在运行中通过CONFIG SET来启用或者禁用。

只读的从服务器会拒绝所有写命令,所以对从服务器不会有误写操作。但这不表示可以把从服务器实例暴露在危险的网络环境下,
因为像DEBUG或者CONFIG这样的管理命令还是可以运行的。不过你可以通过使用rename-command命令来为这些命令改名来增加安全性。

你可能想知道为什么只读限制还可以被还原,使得从服务器还可以进行写操作。虽然当主从服务器进行重新同步或者从服务器重启后,
这些写操作都会失效,还是有一些使用场景会想从服务器中写入临时数据的,但将来这个特性可能会被去掉。

限制有N个以上从服务器才允许写入

从Redis 2.8版本开始,可以配置主服务器连接N个以上从服务器才允许对主服务器进行写操作。但是,因为Redis使用的是异步主从复制,
没办法确保从服务器确实收到了要写入的数据,所以还是有一定的数据丢失的可能性。

这一特性的工作原理如下:
1)从服务器每秒钟ping一次主服务器,确认处理的复制流数量。
2)主服务器记住每个从服务器最近一次ping的时间。
3)用户可以配置最少要有N个服务器有小于M秒的确认延迟。
4)如果有N个以上从服务器,并且确认延迟小于M秒,主服务器接受写操作。

还可以把这看做是CAP原则(一致性,可用性,分区容错性)不严格的一致性实现,虽然不能百分百确保一致性,但至少保证了丢失的数据不会超过M秒内的数据量。

如果条件不满足,主服务器会拒绝写操作并返回一个错误。
1)min-slaves-to-write(最小从服务器数)
2)min-slaves-max-lag(从服务器最大确认延迟)

通过redis实现服务器崩溃等数据恢复

由于redis存储在内存中且提供一般编程语言常用的数据结构存储类型,所以经常被用于做服务器崩溃宕机的数据恢复处理。服务器可以在某些指定过程中将需要保存的数据以json对象等方式存储到redis中,也就是我们常说的快照,当服务器运行时读取redis来判断是否有待需要恢复数据继续处理的业务。当一次业务处理结束后再删除redis的数据即可。redis提供两种将内存数据导出到硬盘实现数据备份的方法:

1)RDB方式(默认)
RDB方式的持久化是通过快照(snapshotting)完成的,当符合一定条件时Redis会自动将内存中的所有数据进行快照并存储在硬盘上。进行快照的条件可以由用户在配置文件中自定义,由两个参数构成:时间和改动的键的个数。当在指定的时间内被更改的键的个数大于指定的数值时就会进行快照。RDB是redis默认采用的持久化方式,在配置文件中已经预置了3个条件:
save 900 1 #900秒内有至少1个键被更改则进行快照
save 300 10 #300秒内有至少10个键被更改则进行快照
save 60 10000 #60秒内有至少10000个键被更改则进行快照

可以存在多个条件,条件之间是"或"的关系,只要满足其中一个条件,就会进行快照。 如果想要禁用自动快照,只需要将所有的save参数删除即可。
Redis默认会将快照文件存储在当前目录(可CONFIG GET dir来查看)的dump.rdb文件中,可以通过配置dir和dbfilename两个参数分别指定快照文件的存储路径和文件名。

Redis实现快照的过程

  • Redis使用fork函数复制一份当前进程(父进程)的副本(子进程);
  • 父进程继续接收并处理客户端发来的命令,而子进程开始将内存中的数据写入硬盘中的临时文件;
  • 当子进程写入完所有数据后会用该临时文件替换旧的RDB文件,至此一次快照操作完成。
  • 在执行fork的时候操作系统(类Unix操作系统)会使用写时复制(copy-on-write)策略,即fork函数发生的一刻父子进程共享同一内存数据,当父进程要更改其中某片数据时(如执行一个写命令 ),操作系统会将该片数据复制一份以保证子进程的数据不受影响,所以新的RDB文件存储的是执行fork一刻的内存数据。

Redis在进行快照的过程中不会修改RDB文件,只有快照结束后才会将旧的文件替换成新的,也就是说任何时候RDB文件都是完整的。这使得我们可以通过定时备份RDB文件来实 现Redis数据库备份。RDB文件是经过压缩(可以配置rdbcompression参数以禁用压缩节省CPU占用)的二进制格式,所以占用的空间会小于内存中的数据大小,更加利于传输。

除了自动快照,还可以手动发送SAVE或BGSAVE命令让Redis执行快照

SAVE或BGSAVE命令的区别

SAVE是由主进程进行快照操作,会阻塞住其他请求
BGSAVE通过fork子进程进行快照操作,不会阻塞主进程。

Redis启动后会读取RDB快照文件,将数据从硬盘载入到内存。根据数据量大小与结构和服务器性能不同,这个时间也不同。通常将一个记录一千万个字符串类型键、大小为1GB的快照文件载入到内 存中需要花费20~30秒钟。 通过RDB方式实现持久化,一旦Redis异常退出,就会丢失最后一次快照以后更改的所有数据。这就需要开发者根据具体的应用场合,通过组合设置自动快照条件的方式来将可能发生的数据损失控制在能够接受的范围。如果数据很重要以至于无法承受任何损失,则可以考虑使用AOF方式进行持久化。

2)AOF方式
默认情况下Redis没有开启AOF(append only file)方式的持久化,可以在redis.conf中通过appendonly参数开启:
appendonly yes
在启动时Redis会逐个执行AOF文件中的命令来将硬盘中的数据载入到内存中,载入的速度相较RDB会慢一些

开启AOF持久化后每执行一条会更改Redis中的数据的命令,Redis就会将该命令写入硬盘中的AOF文件。AOF文件的保存位置和RDB文件的位置相同,都是通过dir参数设置的,默认的文件名是appendonly.aof,可以通过appendfilename参数修改:
appendfilename appendonly.aof
配置redis自动重写AOF文件的条件

auto-aof-rewrite-percentage 100 # 当目前的AOF文件大小超过上一次重写时的AOF文件大小的百分之多少时会再次进行重写,如果之前没有重写过,则以启动时的AOF文件大小为依据

auto-aof-rewrite-min-size 64mb # 允许重写的最小AOF文件大小
配置写入AOF文件后,要求系统刷新硬盘缓存的机制

 
# appendfsync always   # 每次执行写入都会执行同步,最安全也最慢
appendfsync everysec   # 每秒执行一次同步操作

# appendfsync no       # 不主动进行同步操作,而是完全交由操作系统来做(即每30秒一次),最快也最不安全

Redis允许同时开启AOF和RDB,既保证了数据安全又使得进行备份等操作十分容易。此时重新启动Redis后Redis会使用AOF文件来恢复数据,因为AOF方式的持久化可能丢失的数据更少

原文出自:
散尽浮华https://www.cnblogs.com/kevingrace/p/5685332.html
扩展阅读,主从复制原理
https://www.cnblogs.com/lukexwang/p/4711977.html#4048751

猜你喜欢

转载自blog.csdn.net/zjcjava/article/details/85256059