主从模式是分布式系统中,多个服务器部署redis的一种方式。
分布式系统主要是解决“单点问题”。
如果某个服务器程序,只有一个节点(只有一个物理服务器,来部署服务器程序)。存在可用性问题,如果这个机器挂了,意味着服务中断。并且单个服务器性能不高,支持的并发量有限。
而在分布式系统中,通过多个服务器来部署redis服务,从而构成一个redis集群。此时就可以让这个集群给整个分布式系统中提供服务,提供更稳定,更高效的数据存储功能。
引入多个服务器(多个节点)分别部署redis-server进程,在若干个redis节点中,其中一个是“主”节点,其他的是“从”节点。从节点的数据要跟随主节点变化,数据要和主节点保存一致。
由于从节点的数据时刻和主节点保持一致,因此其他客服端从 从节点读取的数据,和从主节点读取的数据,没有区别。后续有客服端来读取数据,就可以从上述节点中,随机挑选一个节点,给客服端提供服务。
如果我修改了从节点的数据,能否把从节点的数据同步到主节点?
redis主从模式中,从节点的数据不允许修改,只能读取
如果从节点挂了,是否有影响?
此时可以从主节点或者其他从节点读取数据
如果主节点挂了,是否有影响?
从节点只能读取数据,如果需要写数据,就无法写入,可用性视乎不是很理想。并且一般不搞多个主节点,因为相互之间进行数据同步,非常麻烦
如果整个redis集群挂了,怎么办?
考虑到更高的可用性,就需要把这些机器放到不同的机房中,异地多活。
主从模式,主要针对“读操作”进行并发量&可用性的提高。而写操作,依赖于主节点,主节点又不能搞多个,所以可用性&并发量并没有得到提升。
redis主从结构,需要启动多个redis服务器,正常来说,每个redis服务器,应该在单独的主机上,但是我只有一个云服务器,因此只能在一个云服务器上,运行多个redis-server进程。
需要保证多个redis-server的端口是不同的,本来redis-server默认的端口是6379,此时新启动的redis-server不能再使用6379
将主节点的的配置信息负责到从节点中
:::tips
cd redis-conf/
cp /etc/redis.conf ./slave1.conf
cp /etc/redis.conf ./slave2.conf
:::
在从节点的配置文件中修改redis端口和运行后台运行
:::tips
vim slave1.conf
vim slave1.conf
:::
启动两个从节点
:::tips
redis-server ./slave1.conf
redis-server ./slave2.conf
:::
配置两个从节点,以6379端口的redis-server为主节点
通过info replication
命令查看当前redis节点的相关状态
使用slaveof no one
命令,断开主从复制关系。
从节点断开主从关系,原有的数据不会丢失,但是后续主节点如果针对数据做出修改,从节点就无法自动同步数据。
slaveof
命令还可以实现切换主节点操作,执行slaveof [newMasterIp] [newMasterPort]
命令即可。
通过slaveof
修改了主从结构,此处的修改是临时性的,如果重新启动了redis服务器,仍然会按照最初配置文件中设置的内容来建立主从结构。
主从节点一般部署在不同机器上,主节点和从节点的网络通信是采用tcp协议,tcp内部支持nagle算法(默认开启),该算法针对小的tcp数据包进行合并,减少了包的个数。
在redis的配置文件中,repl-disable-tcp-nodelay
参数用于主从同步通信过程中,选择no关闭tcp的nagle算法,减少传输时延;选择yes则开启tcp的nagel算法,节省网络带宽。
redis进行主从复制的拓扑结构可以支持单层或多层复制关系,根据拓扑复杂性可以分为三种:一主一从,一主多从,树状主从结构。
如果写数据请求太多,就会给主节点造成一些压力
可以通过关闭主节点的aof,只开启从节点的aof,缓解压力。
如果主节点挂了,不能让主节点自动重启,如果自动重启,此时没有aof文件,就会丢失数据,进一步主从同步,连同从节点的数据也给删除了
改进方法:当主节点挂了,就让主节点从从节点获取到aof文件,再启动
在实际开发中,读请求远高于写请求。一主多从结构可以把读命令负载均衡到不同的从节点上分担压力,同时一些耗时的读命令还可以指定一台专门的从节点来执行。
但是由于从节点较多,主节点完成写请求,同步给从节点需要耗费大量的网络带宽。
树状的主从结构可以减少主节点同步压力,只是同步的延时更长了。
requirepass
参数,则需要密码验证,从节点通过配置masterauth
参数设置密码,如果验证失败,则从节点的复制将会停止。psync
不需要咱们手动执行,redis服务器会在建立好主从同步关系之后,自动执行psync
。从节点负责执行psync
,从主节点这边拉取数据。
语法格式:psync replicationid offset
由主节点生成(主节点启动的时候会生成,从节点晋升成主节点也会生成),即使同一个主节点,每次重启,生成的replicationid
都是不同的。当从节点和主节点建立了复制关系,就会从主节点这边获取到replicationid
。我们可以通过info replication
获取当前replicationid
的值。
假设主节点A,从节点B。主节点A生成replid,从节点获取到A的replid。当A和B通信过程中发生网络抖动,B可能误认为A挂了,B就会自己成为主节点(给自己生成一个replid)。此时B还会记得之前旧的replid,即用replid2存储。后续网络稳定了,B就可以根据replid2重新回到A的怀抱。
psync
可以从主节点获取全量数据,也可以获取一部分数据。主要是看offset
怎么写,offset
写作-1,就是获取全量数据,offset
写作具体的正整数,则是从当前偏移量位置往后获取。获取全量数据是最稳妥的,但是也是比较低效。如果从节点之前已经从主节点复制过一部分数据,就只需要将之前没有复制过的数据复制过来即可。
并不是从节点索要哪部分,主节点就会给哪部分,主节点会自行判定,看当前是否方便给部分数据,不方便就给全量。
什么时候进行全量复制
什么时候进行部分复制
从节点之前已经从主节点复制过数据,因为网络抖动或者从节点重启,从节点需要重新从主节点同步数据,此时看能不能只同步一小部分(大部分数据都是一致)
psync
命令给主节点进行数据同步,由于是第一次进行复制,从节点没有主节点的replicationid
和offset
,所以发送psync ? -1
获取全量数据。+FULLRESYNC
响应bgsave
进行RDB文件的持久化(rdb是二进制格式,节省空间),不使用原有的rdb文件,而是重新生成rdb快照,因为已有的rdb文件可能和当前最新的数据存在较大差异bgrewriteaof
重写,进行整理。bgsave
生成rdb文件的过程,直接将数据传输给从节点,节省了一系列读写硬盘的操作虽然引入“无硬盘模式”省去了一系列读写硬盘操作,但是全量复制的整个过程仍然比较重量,相比网络传输而言,读写硬盘是个小头。
从节点要从主节点进行全量复制,开销很大,有些时候,从节点本身已经持有主节点的大部分数据,这个时候就不太需要进行全量复制了。例如,网络抖动,主节点最近修改的数据无法及时同步过去,而网络抖动,一般是暂时性的,当从节点和主节点重新建立连接以后,就需要进行数据同步。
部分复制流程图
repl-timeout
时间,主节点会认为从节点故障并中断复制连接积压缓冲区:由数组构成的一个循环队列,记录的总量有限,随着时间推移,会把前面的数据逐渐覆盖掉。
replicationid
和offset
作为psync
的参数发送给主节点,请求进行部分复制。replicationid描述”数据来源“,offset描述”数据的复制进度“
:::warning
主节点会先判定replicationid
,如果和自己的id不一样,说明以前不具备主从关系,直接全量复制。如果replicationid
一样,再判定offset
,如果从节点的进度再当前的积压缓冲区之内,进行部分复制;如果当前从节点的进度超出积压缓冲区的范围,继续全量复制。
:::
psync
请求后,进行必要验证,随后根据offset
去复制积压缓冲区查找合适的数据,并响应+CONTINUE
给从节点。全量复制是从节点刚连上主节点之后,进行的数据初始化工作;部分复制是全量复制的特殊情况,是对全量复制的优化,目的和全量复制一样。而实时复制是从节点已经和主节点同步好了数据,但是之后,主节点会不断收到新的修改数据的请求,需要同步给从节点。
从节点和主节点之间建立TCP长连接,然后主节点把自己收到的修改数据的请求,通过上述连接,发送给从节点再根据这些修改请求,修改内存中的数据
上述过程正常来说,延时比较短,但是如果是多级从节点的树形结构,延时也会上升
在进行实时复制的时候,需要保证连接处于可用状态。依靠”心跳包“机制来检查
最大的问题是在主节点上,当主节点挂了,虽然还能够提供读操作,但是从节点无法自动升级成主节点,替换原有主节点对应的角色,需要程序员手动恢复主节点。而自动替换主节点需要redis的哨兵机制。