Redis的主从复制

发布于:2024-04-25 ⋅ 阅读:(89) ⋅ 点赞:(0)

引入:分布是系统涉及到一个非常关键的问题:单点问题(如果摸个服务器程序,只有一个节点(只搞一个物理服务器,来部署这个服务器程序)会出现:

1.可用性问题,如果这个机器挂了,意味着服务器就中断了

2.性能/支持的并发量也是有限的

引入分布式系统,主要也是为了解决上述的单点问题

在分布式系统中,往往希望有多个服务器来部署redis服务,从而构成一个redis集群,此时就可以让这个集群给整个分布式系统中的其他的服务,提供更稳定/更高效的数据存储功能

在分布式系统中,希望使用多个服务器来部署redis,存在一下几种redis的部署方式:

1.主从模式

2.主从+哨兵模式

3.集群模式

一.主从模式:在若干个redis节点中,有的是“主”节点,有的节点是“从”节点(从节点得听主节点的(从节点上的数据要跟随主节点变化;从节点的数据 要和主节点保持一致))

本来,主节点上保存一堆数据,引入从节点后,就是要把主节点上面的数据,复制出来,放到从节点中,后续主节点这边对数据有任何修改,就会把这样的修改给同步到从节点上

注:如果修改了从节点的数据,是否把从节点的数据同步到主节点上呢?

答:redis主从模式中,从节点上的数据,不允许修改,只能读取数据

注:

①之前只是单个redis服务器节点,此时这个机器挂了,整个redis就挂了;上述这个主从结构,这写redis的机器就不太可能同时就挂了

②整个机房是有可能被一锅端的,这时候如果考虑到更高的可用性,就可以把这些机器放到不同的机房中(异地多活)

③如果是挂掉了某个从节点,没啥影响,此时继续从主节点或其他从节点读取数据,得到的效果完全相同

④如果是挂掉了主节点,还是有一定影响的,从节点只能读取数据。如果要写数据就没得写了(可用性是提高了,但是还没有到非常理想的程度)

⑤由于从节点的数据和主节点的数据保持一致,因此其他的客户端从从节点这里读取数据和从主节点这里读取数据是一样的,没有区别,后续如果有客户端来读取数据,就可以从上述节点中随机挑选一个节点,给这个客户端提供读取数据的服务

⑥引入更多的计算资源,自然能支撑的并发量也大幅度提高了

配置主从结构:

首先要启动多个redis服务器,正常来说每个redis服务器程序应该在一个单独的主机上(才是分布式),当前只有一个云服务器的话,可以在云服务器上运行多个redis-server进程的

本来redis-server默认的端口号是6379,此时就不能让新启动的redis-server再继续使用6379了,就要保证多个redis-server端口号是不同的

如何指定redis的端口号?

1.可以在启动程序的时候,通过命令行来指定端口号--port选项

2.也可以直接在配置文件中来设定端口号即可(建议用这个)

配置多个从节点:

配置端口号:port  端口号

daemonize  yes:按照后台进程的方式来运行

开始配置成主从结构:

1.在配置文件中加入 slaveof {masterHost} {masterPort}随redis启动生效(修改了配置文件,一致持久生效)                                                                                                                                          2.在redis-server启动命令加入--slaveof {masterHost} {masterPort}                                                    3.直接使用redis命令:redis {masterHost}{masterPort}生效

注:redis修改完配置,需要重新启动才能生效(这种停止redis-server的方式是和之前通过直接运行redis-server命令的方式搭配的;而如果使用service redis-server start这种方式启动,就必须使用service redis-server stop来进行停止,此时如果使用kill -9的方式停止,kill掉之后,这redis-server进程能自动启动)

服务器就是要稳定性和高可用,但是其他服务器上的某些程序,可能也难以避免出现挂了的情况,如果服务器进程挂了,要是能自动重启进程(把进程再启动起来),对于整体的服务器不会产生严重影响(往往会有另外一个进程来专门监控指定的服务器进程的运行状态)

这里6379和38311 或者6379和38598是其中一个redis从节点,和主节点之间的TCP连接,从节点启动之后就会和主节点建立上TCP连接,主节点就相当于服务器,从节点就相当于客户端

注:

①主节点这边的数据产生的任何修改,从节点就能立刻感知到,就是刚才看到的这些TCP连接起到的效果

②从节点不能写数据

role:master:主节点上执行

connected_slaves:2:连接2个从节点

slave0:ip=127.0.0.1,port=6380,state=online,offset=4748,lag=1:port:端口号;online:连接状态;offset:就相当于是从节点和主节点之间,同步数据的进度(主节点上会收到源源不断地“修改数据”请求,从节点这里就需要从主节点这里同步这些修改请求,从节点和主节点之间数据同步,不是瞬间完成的)lag:是否在线

master_replid:f39a5c8c6b677dc62f1b30691e5047a01ea13aed:主从节点完成主从复制的身份便是(主从节点的值是一样的)

master_replid2:0000000000000000000000000000000000000000:如果出现掉线的情况下,会记录上一次主节点的replid值

master_repl_offset:4748   second_repl_offset:-1:同步数据的偏移量

repl_backlog_active:1,repl_backlog_size:1048576,repl_backlog_first_byte_offset:1,repl_backlog_histlen:4748:积压缓冲区,支持部分同步机制实现

用slaveof no one(直接在redis客户端中的命令):直接使用这个命令断开现有的主从复制关系,从节点断开主从关系,它就不属于其他节点了,里面已经有数据,是不会抛弃的;但是后续主节点如果针对数据做出修改,从节点就无法再自动同步数据了


刚才是通过slaveof是修改了主从结构,此处的修改是临时性的,如果重新启动了redis服务器,仍然会按照最初配置中的内容来建立主从关系

此时6380看起来像是个主节点,实际上不是,仍然是个从节点,只是作为6381同步数据的来源,自身仍然是不能修改数据的

注:

①“主从复制”是从主到从,不是从 从节点到主节点,如果允许从节点修改数据,后续主节点是感知不到的,那么就会导致数据不一致

②主节点和从节点之间通过网络来传输(tcp)

③TCP内部支持了nagle算法(默认开启),开启了就会增加TCP的传输延迟,节省了网络带宽;关闭了就会减少TCP的传输延迟,增加了网络带宽;目的和TCP的捎带应答是一样的,针对小的TCP数据,进行合并,减少了包的个数

④reply-disable-tcp-nodelay,这个选项可以用于主从同步通信过程中,关闭TCP的nagle算法,从节点更快速的和主节点同步

拓扑结构:若干个节点之间,按照啥样的方式来进行组织连接

如果写数据请求太多,此时也是会给主节点造成一些压力 ;此时可以通过关闭主节点的AOF,只在从节点上开启AOF(但是,这种设定,有一个严重的缺陷,主节点一旦挂了,不能让他自动重启(如果自动重启,此时没有AOF文件,就会丢失数据,进一步的主从同步,会把从节点的数据也给删了))

改进办法:当主节点挂了之后,就需要让主节点从从节点这里获取到AOF的文件,再启动

实际开发中,读请求远远超过写请求

主节点的数据发生改变,就会把改变的数据同时同步给所有的从节点,随从节点的个数的增加,同步一条数据,就需要传输多次(主节点需要很多的网卡宽带)

这样主节点就不需要那么高的网卡宽带,一旦数据进行修改了,同步的延时是比刚才更长的

数据的同步过程:

redis提供了psync命令,完成数据同步(psync不需要我们手动执行,redis服务器会在建立好主从同步关系之后,自动执行psync,从节点负责执行psync,从节点从主节点这边拉去数据)

psync  replicationid offset

1.replication复制:

这个是主节点生成的,主节点启动的时候就会生成,从节点晋升成主节点也会生成(即便是同一个主节点,每次重启,生成的replicationid都是不同的);从节点和主节点建立了复制关系,就会从主节点这边获取到replication  id (当前,此处讨论的都是一个主节点,多个从节点;后面谈到集群的时候,多个主节点,多个从节点)

一般情况下热replid2这个是用不上的

有一个主节点A(生成replid),还有一个从节点B(获取到A的replid)                                             如果A和B通信过程中出现了一些网络都懂抖动,B可能就会认为A挂了,B就会自己成为主节点(给自己生成一个replid)(此时B也会记得,之间旧的replid,就是通过replid2),后续网络稳定了,B还可以更具replid重新回到A的怀抱(需要手动干预;如果通过哨兵机制,就可以自动完成这个过程)

2.offset偏移量:主节点和从节点上都会维护偏移量(整数)                                                             主节点的偏移量:主节点上会收到很多的修改命令,每个命令都要占据几个字节,主节点会把这些修改命令,每个命令的字节数进行累加                                                                                           从节点的偏移量:描述了现在从节点这里的数据同步到了哪里了                                                       如果从节点这里的偏移量和主节点这里的偏移量一样了,就开始实时同步了

从节点(slave)每秒钟上报⾃⾝的复制偏移量给主节点,因此主节点也会保存从节点的复制偏移量:slave0:ip=127.0.0.1,port=6380,state=online,offset=1055214,lag=1

获取数据

psync这里可以从主节点上获取全量数据,也可以获取部分数据                                                        主要就是看offset这里的进度:offset写作-1或则?就是获取全量数据;offset写具体的正整数,则是从当前便宜两的位置来进行获取

获取全量数据是最稳妥的,但是会比较低效,如果从节点已经从主节点复制过一部分数据了,就只需要把新的之前没有复制过的数据搞到从节点哪里即可(不是从哪节点索要那部分,主节点就一定给那部分,主节点会自行判断,看当前是否方便给部分数据,不方便就只能给全量)

1)从节点发送psync命令给主节点,replid和offset的默认值分别是?和-1.

2)主节点根据psync参数和⾃⾝数据情况决定响应结果:

• 如果回复+FULLRESYNCreplidoffset,则从节点需要进⾏全量复制流程。(全量数据的同步)

• 如果回复+CONTINEU,从节点进⾏部分复制流程。(增量数据的同步)

• 如果回复-ERR,说明Redis主节点版本过低,不⽀持psync命令。从节点可以使⽤sync命令进⾏ 全量复制。

   • psync⼀般不需要⼿动执⾏.Redis会在主从复制模式下⾃动调⽤执⾏.

   • sync会阻塞redisserver处理其他请求.psync则不会.

什么时候进行全量复制:

1.首次和主节点进行数据同步

2.首节点不方便进行部分复制的时候

什么时候回进行部分复制:

从节点之间已经从主节点上复制过数据了,因为网络都懂或者从节点重启了,从节点需要重新从主节点这边同步数据,此时看能不能只同步一小部分数据(大部分数据都是一致的)

全量数据的同步流程:

1)从节点发送psync命令给主节点进⾏数据同步,由于是第⼀次进⾏复制,从节点没有主节点的运 ⾏ID和复制偏移量,所以发送psync?-1。

2)主节点根据命令,解析出要进⾏全量复制,回复+FULLRESYNC响应。

3)从节点接收主节点的运⾏信息进⾏保存。(保存必要的信息)

4)主节点执⾏bgsave进⾏RDB⽂件的持久化。(bgsave,生成rdb文件,rdb是二进制格式,比较节省空间;这里不能使用已有的rdb文件,而是必须要重新生成一下,已有的rdb文件可能会和当前最新的数据存在较大差异)

5)从节点发送RDB⽂件给从节点,从节点保存RDB数据到本地硬盘。

6)主节点将从⽣成RDB到接收完成期间执⾏的写命令,写⼊缓冲区中,等从节点保存完RDB⽂件 后,主节点再将缓冲区内的数据补发给从节点,补发的数据仍然按照rdb的⼆进制格式追加写⼊到收 到的rdb⽂件中.保持主从⼀致性。(在主节点生成rdb文件和传输rdb文件的过程中,还会继续收到很多新的修改操作,新修改的数据也必须要同步给从节点,当从节点收完了主节点发来的rdb之后,主节点会把这些新修改的操作也发送给从节点)

7)从节点清空⾃⾝原有旧数据。

8)从节点加载RDB⽂件得到与主节点⼀致的数据。

9)如果从节点加载RDB完成之后,并且开启了AOF持久化功能,它会进⾏bgrewrite操作,得到最(如果从节点已经开启了AOF,在上述加载数据过程中,从节点就会产生很多的AOF日志;由于当前收到的是大批量的数据,此时产生的AOF日志,整体来说,可鞥会存在一定的冗余信息啥的,因此针对AOF日志进行整理,也是必要的过程)

主节点,进行全量复制的时候也支持“无硬盘模式”(diskless)

主节点生成的rdb二进制数据不是直接保存到文件中,而是直接进行网络传输(省下来一系列度硬盘和写硬盘的操作)

从节点之前,也是先把收到的rdb数据写入到硬盘中,然后再加载,现在也可以省略这个步骤,直接把数据进行加载了

即使引入了无硬盘模式,仍然整个操作是比较重量,比较耗时的,网络传输是没发省的,相比于网络传输,读写硬盘是小头

注:

replication id/replid         runid

一个服务器上,replicationid和run id都是存在的,两个不同的id  看起来非常像

主节点:info  replication:

              info  server:

从节点:info replication:

              info  server:

replid和runid长度相同,格式也非常相似

run id是每个节点都不相同的

replid则是具有主从关系的节点,是相同的

官方文档上说主从复制是用replid

部分数据复制:

从节点要从主节点这里进行全量复制,全量复制开销是很大的,有些时候,从节点本来就持有主节点的部分数据,这个时候,就不填需要进行全量复制了(比如出现网络抖动,主节点这边最近修改的数据可能就无法及时同步过来,更严重的,从节点已经感知不到主节点了(进一步的从节点可能就会升级成主节点);网络抖动都是暂时的,过一会儿就恢复过来了,此时就可以让主节点和从节点重新建立联系;当从节点和主节点重新建立联系,就需要进行数据的同步了)

psync带有具体的replid和offset值,主节点就要根据psync的参数进行判定,当前这次是按照全量复制,花式部分复制合适

1)当主从节点之间出现⽹络中断时,如果超过repl-timeout时间,主节点会认为从节点故障并终端 复制连接。

2)主从连接中断期间主节点依然响应命令,但这些复制命令都因⽹络中断⽆法及时发送给从节点,所 以暂时将这些命令滞留在复制积压缓冲区中。(就是内存中简单的队列,会记录最近一段时间修改的数据,总量有限,随着时间的推移,就会把之前的就得数据逐渐删掉)

3)当主从节点⽹络恢复后,从节点再次连上主节点。

4)从节点将之前保存的replicationId和复制偏移量作为psync的参数发送给主节点,请求进⾏部分 复制。(replicationid其实就是在描述“数据的来源”,offset描述“数据的复制进度”   比如:吕布;如果replicationid不一样,说明这小子就是专捅义父的料,就得好好教育一下,需要全量复制;如果replicationid一样,这小子之前就是自己的义子,就不需要那么复杂的教育了 ;offset再判定,看这小子最近落下的功课多不多,offset表示从节点之前同步数据的进度是如何,主节点就看这个进度是否在当前的挤压缓冲区内,如果在积压缓冲区内,此时就可以直接进行部分复制,就只把最近这段时间的数据复制过去即可,如果确实当前从节点的进度已经超出积压缓冲区的范围了,就全量复制)

5)主节点接到psync请求后,进⾏必要的验证。随后根据offset去复制积压缓冲区查找合适的数据, 并响应+CONTINUE给从节点。

6)主节点将需要从节点同步的数据发送给从节点,最终完成⼀致性。 如 果某个课件传输失败了,助教可以单独要这个缺失的课件.

全量复制:从节点刚连上主节点之后,进行数据的初始化工作

部分复制:全量复制的特殊情况,优化手段,目的和全量复制一样

实时复制:从节点已经和主节点,同步好了数据(从节点这一刻已经和主节点数据一致了),但是之后,主节点这边会源源不断地收到新的修改数据的请求,主节点上的数据就会随之改变,也需要能够同步给从节点(从节点和主节点之间会建立TCP的长连接,然后主节点把自己收到的修改数据的请求,通过上述连接,发送给从节点,从节点再根据这些修改请求,修改内存中的数据

在进行实时复制的时候,需要保证连接处于可用状态(心跳包机制)

心跳包机制:

主节点:默认,每个10s给从节点发送一个ping命令,从节点收到就返回pong

从节点:默认,每隔1s就给主节点发起一个特定的请求,就会上报当前从节点复制数据的进度(offset)