当前位置: 首页 > 后端技术 > Java

关于Redis主从复制原理,史上最佳文章!

时间:2023-04-02 00:53:56 Java

和Mysql主从复制的道理是一样的。Redis虽然读写速度很快,但是也会造成特别大的读压力。为了分担读压力,Redis支持主从复制。Redis的主从结构可以采用主从或级联结构。Redis主从复制根据是否全量可以分为全量同步和增量同步。下图显示了级联结构。全量同步Redis全量复制一般发生在Slave初始化阶段。这个时候Slave需要把Master上的所有数据都拷贝过来。具体步骤如下:-从服务器连接到主服务器,发送SYNC命令;-masterserver收到SYNC名称后,开始执行BGSAVE命令生成RDB文件,并使用buffer记录此后执行的所有写命令;-主服务器执行BGSAVE后,向所有从服务器发送快照文件,并在发送过程中继续记录执行的写命令;-从服务器接收快照文件后丢弃所有旧数据,并加载接收到的快照;从服务器发送缓冲区中的写入命令;-从服务器加载完快照,开始接收命令请求,从主服务器缓冲区执行写命令;![](https://upload-images.jianshu...完成以上步骤后,从服务器数据初始化的所有操作就完成了,此时从服务器可以接收用户的读请求。增量同步Redis增量复制是指发生同步写操作到从服务器的过程,增量复制的过程主要是主服务器每执行一次写命令就向从服务器发送相同的写命令,而从服务器server接收并执行接收到的写命令Redis主从同步策略主从刚连接时,进行全量同步;全量同步结束后,进行增量同步,当然如果需要,slave也可以随时发起全量同步,redis的策略是,无论怎样,都会先尝试进行增量同步,卷同步,如果不成功,则需要slave进行全量同步。注意,如果多个Slave断开连接,需要重启,因为只要Slave启动,就会发送sync请求与master同步。当多个同时发生时,可能会导致MasterIO宕机时间急剧增加。Redis主从复制的配置非常简单,它可以让从服务器成为主服务器的完整副本。Redis主从复制的一些重要内容需要明确:1)Redis使用异步复制。但是从Redis2.8开始,从服务器会周期性的回复复制流处理的数据量。2)一个主服务器可以有多个从服务器。3)从服务器也可以接受来自其他从服务器的连接。除了多个从服务器连接到一个主服务器之外,多个从服务器也可以连接到一个从服务器,形成图结构。4)Redis主从复制不阻塞主服务器。也就是说,当多个从服务器都在进行初始同步时,主服务器仍然可以处理请求。5)主从复制不阻塞从服务器。当从服务器做初始同步时,它使用旧版本的数据来响应查询请求,假设你在redis.conf配置文件中配置它。否则,您可以配置从服务器在复制流关闭时向客户端返回错误。但是,当初始同步完成后,需要删除旧的数据集,加载新的数据集。在这短暂的时间内,从服务器将阻止传入的请求。6)主从复制可用于增强扩展性,使用多个从服务器来处理只读请求(例如,繁重的排序操作可以放在从服务器上),或者简单地用于数据冗余。7)使用主从复制可以节省主服务器写数据到磁盘的消耗:在主服务器的redis.conf文件中配置“避免保存”(注释掉所有“保存”命令),然后连接一个从服务器配置为“保存”。但是这个配置必须保证主服务器不会自动重启(更多信息请阅读下一段)。主从复制的一些特点:1)异步复制;2)一个masterredis可以包含多个slaveredis;3)每个A从redis都可以接收到其他从redis服务器的连接;4)主从复制对于主redis服务器来说是非阻塞的,也就是说当从服务器在进行主从复制同步的过程中,主redis仍然可以处理外部请求的访问请求;5)主从复制对于从redis服务器来说也是非阻塞的,也就是说即使从redis在主从复制过程中,也可以接受外部的查询请求,但是此时从redis返回是如果不想对旧数据这样做,可以在启动redis时在配置文件中设置,那么在从redis复制同步的过程中,外界的查询请求会返回错误给客户端;(虽然主从复制过程中,从redis是非阻塞的,但是当从masterredis同步最新数据时,需要将新的数据加载到内存中,加载到内存的过程是blocked.在这段时间内,请求会被阻塞,但即使是大数据集,加载到内存也需要很长时间);6)主从复制提高了redis服务的扩展性,避免了单台redis服务器读写访问压力过大的问题,同时也可以为数据备份和冗余提供解决方案;7)为了编码主redis服务器磁盘压力带来的开销,可以配置让主redis不持久化数据到磁盘,但是允许一个配置好的从redis服务器将相关数据持久化到磁盘及时,但是会出现一个问题,一旦redis主服务器重启,由于redis主服务器的数据为空,此时主从同步可能会导致从redis服务器失效。数据也被清除;Redis是如何实现主从同步的?全同步:主服务器会启动一个后台进程,将redis中的数据生成一个rdb文件。同时,服务端会缓存所有从客户端接收到的写命令(包括增删改查),并在后台保存,处理完成后将rdb文件传给从服务器,而从服务器会将rdb文件保存在磁盘上,并通过读取文件将数据加载到内存中,之后主服务器会将这段时间的命令缓存起来,通过redis传输协议将它们发送给从服务器,然后从服务器将这些命令依次作用于自己的本地数据集,最终实现数据一致性。部分同步:redis2.8之前不支持部分同步。当主从服务器连接断开时,主从服务器会进行全量数据同步,但是从redis2.8开始,即使主从连接中途断开,也不需要进行全同步,因为这个版本开始引入了部分同步的概念。部分同步的实现依赖于在主服务器的内存中维护每个从服务器的同步日志和同步ID。每个从服务器在与主服务器同步时都会携带自己的同步ID和上次同步的最后位置。当主从连接断开时,从服务器会每隔一段时间(默认1s)主动尝试与主服务器连接。如果从服务器携带的偏移量标识还在主服务器上的同步备份日志中,则从从服务器发送过来的偏移量开始继续上次的同步操作。如果slave发送的offset已经不在master的同步备份日志中(可能是因为master和slave之间的连接时间太长或者master服务器在短时间内收到了offset)。大量写操作),必须执行完整更新。在部分同步过程中,Master会将本地记录的同步备份日志中记录的指令发送给Slave,以实现数据的一致性。主从同步需要注意的几个问题1)在上面的全同步过程中,master会将数据保存在rdb文件中,然后发送给slave服务器,但是如果master上的磁盘空间不足怎么办已验证?那么此时所有的同步对于master来说将会是一个非常有压力的操作。这时候可以通过无盘拷贝来达到目的,master直接开一个socket将rdb文件发送给slave服务器。(无盘复制一般在磁盘空间有限但网络状况良好的情况下使用)2)主从复制结构,一般从服务器不能进行写操作,但这并不是死机,这样做的原因是为了保证master和slave更容易实现slave之间数据的一致性,如果修改了slave服务器上的数据,那么就需要保证所有的master-slave服务器是一致的,这在结构和处理逻辑上可能更负责。但是,你也可以通过配置文件让从服务器支持写操作。(不过影响要自己承担...)3)master和slave服务器之间会有定时调用,但是如果master设置了密码,那么如果slave不设置密码,slave将无法对master进行任何操作,所以如果master服务器上有密码,那么slave也要设置相应的密码(通过在配置文件中设置masterauth);4)关于从服务器上过期key的处理,主服务器负责key的过期删除,然后将相关删除命令同步数据同步到从服务器,从服务器删除本地key根据删除命令。主服务器不持久时复制的安全性。为什么非持久主服务器的自动重启很危险?为了更好地理解问题,请参见以下故障示例,其中主从数据库均被删除。将节点A设为主服务器,关闭持久化,节点B和C从节点A复制数据。此时出现了崩溃,但是Redis有自动重启系统,重启进程,因为持久化关闭了,节点重启后只有一个空数据集。节点B和C正在从节点A复制,现在节点A是空的,所以节点B和C上的复制数据也会被删除。当在主服务器上关闭持久性并允许自动重启的高可用性系统中使用RedisSentinel时,这种情况很危险。比如主服务器可能在短时间内完成重启,导致Sentinel检测不到这个故障,就会出现上面提到的故障。如果数据比较重要,在使用主从复制时关闭主服务器持久化功能的场景下,应该关闭实例自动重启。Redis主从复制是如何工作的如果你设置了一个从服务器,在连接时它发送一个SYNC命令,不管它是第一次连接还是重新连接。然后主服务器启动后台存储,并启动缓存用于修改来自新连接的数据的命令。当后台存储完成后,主服务器将数据文件发送给从服务器,从服务器将其保存在磁盘上,然后加载到内存中。然后主服务器将刚刚缓存的命令发送给从服务器。这是作为命令流完成的,并且采用与Redis协议本身相同的格式。您可以通过telnet自己尝试一下。在Redis服务器工作时连接Redis端口,发送SYNC命令,会看到批量传输,主服务器收到的每条命令都会通过telnet会话重新发送。当主从服务器之间的连接由于某种原因断开时,从服务器可以自动重新连接。当多个从服务器同时请求同步时,主服务器只进行一次后台存储。当连接断开重连时,一般会进行一次完整的重同步,但从Redis2.8开始,也可以只重同步一部分。部分重新同步从Redis2.8开始,如果遇到断线,重新连接后可以从中断处恢复复制,无需重新同步。它是这样工作的:master维护复制流的内存积压。主从服务器都维护一个复制偏移量(replicationoffset)和masterrunid。当连接断开时,从服务器会重新连接到主服务器,然后请求继续复制。如果主从服务器的两个masterrunid相同,并且指定的偏移量在内存缓冲区中仍然有效,则复制将从上次中断的点继续。如果其中一个条件不满足,则会进行全量重同步(2.8版本之前,直接进行全量重同步)。因为主运行id没有保存在磁盘中,所以如果从服务器重启,就只能全同步了。部分重新同步这个新功能在内部使用PSYNC命令,旧的实现使用SYNC命令。Redis2.8版本可以检测其连接的服务器是否支持PSYNC命令,不支持则使用SYNC命令。DisklessReplication通常,完全重新同步需要在磁盘上创建一个RDB文件,然后加载该文件以将数据发送到从服务器。如果使用比较慢的磁盘,这个操作会给主服务器带来更大的压力。Redis从2.8.18版本开始尝试支持无盘复制。使用该设置时,子进程直接通过网络将RDB发送给从服务器,而不使用磁盘作为中间存储。配置主从复制非常简单:只需在从服务器的配置文件中添加以下行即可。slaveof192.168.1.16379当然需要把192.168.1.16379换成你自己的主服务器IP(或hostname主机名)和端口。另外,可以调用SLAVEOF命令,主服务器会开始和从服务器同步。关于部分重新同步,还有用于复制内存缓冲区的优化参数。有关详细信息,请参阅Redis媒体中的Redis.conf示例。使用repl-diskless-sync配置参数启用无盘复制。使用repl-diskless-sync-delay参数配置传输开始的延迟,以便等待来自服务器的更多连接。有关详细信息,请参阅Redis媒体中的Redis.conf示例。只读从服务器从Redis2.6开始,从服务器支持只读模式,并且是默认模式。此行为由Redis.conf文件中的slave-read-only参数控制,可以在运行时通过CONFIGSET启用或禁用。只读从站将拒绝所有写命令,因此不会有对从站的错误写入。但这并不意味着从服务器实例可以暴露在危险的网络环境中,因为DEBUG或CONFIG等管理命令仍然可以运行。但是,您可以使用rename-command命令重命名这些命令以增加安全性。您可能想知道为什么可以恢复只读限制,允许从机写入。虽然这些写操作在主从服务器重新同步或者从服务器重启的时候会失败,但是还有一些使用场景是想从服务器写临时数据,但是这个特性以后可能会被去掉。只有超过N个从服务器才允许写入。从Redis2.8版本开始,可以配置master服务器连接N个以上的slave服务器,允许对master服务器进行写操作。但是由于Redis采用的是异步主从复制,没有办法保证从服务器确实收到了要写入的数据,所以还是有一定的数据丢失的可能。此功能的工作原理如下:1)从服务器每秒ping主服务器以确认处理的复制流数。2)主服务器记住每个从服务器的最后一次ping时间。3)用户可以配置至少N台服务器的确认延迟小于M秒。4)如果从服务器多于N个,且确认延迟小于M秒,则主服务器接受写操作。也可以看作是CAP原则(一致性、可用性、分区容错性)的松散一致性实现。虽然不能保证100%的一致性,但至少保证丢失的数据不会超过M秒内的数据量。如果不满足条件,主服务器将拒绝写入操作并返回错误。1)min-slaves-to-write(从服务器的最小数量)2)min-slaves-max-lag(来自服务器的最大确认延迟)通过redis实现服务器崩溃等数据恢复,因为redis存储在内存中和提供了通用编程语言常用的数据结构存储类型,因此常用于服务器崩溃的数据恢复处理。服务器可以在某些指定的过程中,将需要保存在redis中的数据以json对象的形式存储起来,也就是我们常说的快照。服务器运行时读取redis,判断是否有业务需要恢复数据继续处理。业务流程完成后,可以删除redis数据。Redis提供了两种将内存数据导出到硬盘进行数据备份的方法:1)RDB模式(默认)RDB模式持久化是通过快照来完成的。当满足某些条件时,Redis会自动将内存中的所有数据进行快照,存储在硬盘上。抓快照的条件可以由用户在配置文件中自定义,由两个参数组成:时间和改变key的次数。当指定时间内更改的键数大于指定值时,将进行快照。RDB是redis默认采用的持久化方式。配置文件中已经预设了三个条件:save9001#如果900秒内至少有1个key发生变化,则进行快照save30010#300秒内至少有10次发生变化,则进行快照save6010000#如果60秒内至少有10000个key被改变,那么快照的条件可以有多个,条件是“或”的关系。只要满足其中一个条件,就会进行快照。如果要禁用自动快照,只需删除所有保存参数即可。默认情况下,Redis会将快照文件存放在当前目录下的dump.rdb文件中(通过CONFIGGETdir查看)。您可以通过配置dir和dbfilename参数来指定快照文件的存储路径和文件名。Redis实现快照的进程——Redis使用fork函数复制当前进程(父进程)的一个副本(子进程);-父进程继续接收和处理来自客户端的命令,同时子进程开始将数据写入内存硬盘中的临时文件;-当子进程写完所有数据后,会用临时文件替换旧的RDB文件,快照操作完成。-在执行fork时,操作系统(类Unix操作系统)会使用写时复制(copy-on-write)策略,即fork函数发生的瞬间,父子进程共享同一个内存数据,当父进程要改变一段数据时(比如执行写命令),操作系统会复制这段数据,保证子进程的数据不受影响,所以newRDB文件存放的是fork执行瞬间的内存数据。Redis在快照过程中不会修改RDB文件,只有在快照结束后才会将旧文件替换为新文件,也就是说RDB文件在任何时候都是完整的。这样我们就可以通过定期备份RDB文件来实现Redis数据库的备份。RDB文件是压缩后的二进制格式(可以配置rdbcompression参数关闭压缩以节省CPU占用),因此占用空间会小于内存中的数据大小,更便于传输。除了自动快照,还可以手动发送SAVE或BGSAVE命令让Redis执行快照。两个命令的区别在于前者由主进程执行,会阻塞其他请求,后者通过fork子进程执行快照。Redis启动后,会读取RDB快照文件,将数据从硬盘加载到内存中。此时间根据数据量的大小和结构以及服务器性能而有所不同。通常,将具有1000万个字符串键的1GB快照文件加载到内存中需要20-30秒。持久化是通过RDB实现的。一旦Redis异常退出,最后一次快照之后改变的所有数据都会丢失。这就需要开发者根据具体的应用,通过组合设置自动快照条件,将可能的数据丢失控制在可接受的范围内。如果数据重要到不能承受任何损失,可以考虑使用AOF方式进行持久化。2)AOF模式Redis默认不开启AOF(appendonlyfile)的持久化,可以通过redis.conf中的appendonly参数开启:appendonlyyes。数据加载到内存中,加载速度比RDB慢。开启AOF持久化后,每执行一条改变Redis中数据的命令,Redis都会将该命令写入硬盘的AOF文件中。AOF文件的保存位置和RDB文件的位置一样,都是通过dir参数设置的。默认文件名为appendonly.aof,可以通过appendfilename参数修改:appendfilenameappendonly.aof配置redis自动重写AOF文件的条件auto-aof-rewrite-percentage100#当当前AOF文件大小超过上次重写时AOF文件大小的百分比,将再次重写。如果之前没有改写过,会使用启动时的AOF文件大小根据auto-aof-rewrite-min-size64mb#允许改写的最小AOF文件大小配置写完AOF文件后,需要的机制systemtorefreshtheharddiskcache#appendfsyncalways#每执行一次写入都会进行同步,最安全也是最慢的appendfsynceverysec#每秒执行一次同步操作#appendfsyncno#不主动执行同步操作,但是完全交给操作系统来处理(也就是30秒一次),最快也是最不安全的Redis允许同时开启AOF和RDB,这样既保证数据安全又可以进行备份等操作好简单。这时候重启Redis后,Redis会使用AOF文件来恢复数据,因为AOF方式的持久化可能造成的数据丢失较少。当你发现自己的才华支撑不住你的志向时,请静下心来好好学习作者:虎已死还有一个狼链接:https://www.cnblogs.com/daofa...链接:博客园补充一、书项目:codeGoogler/ProgramBooks2:视频教程:程序员必读Java书籍SpringBoot、Spring、Mybatis、Redis,对RabbitMQ、SpringCloud、高并发感觉不错的朋友记得点赞收藏,精选技术文章会持续更新将来!