在分布式系统中,为了解决单点问题,通常会把redis服务部署到多个服务器上,满足故障恢复和负载均衡等请求。
所谓的单点问题,就是某个服务器程序,只有一个节点(只有一个物理服务器,来部署这个服务器程序)。
存在的问题:
可用性问题:如果这个机器挂了,那么部署的redis服务就挂了,服务就中断了。
性能/支持的并发量有限:毕竟一台主机的硬件资源(比如CPU,硬盘,网络带宽等等)是有限的,一个请求就要消耗一定量的硬件资源。一旦并发量太高,如果请求超过主机所能提供这些资源,那么可能就会出现异常,甚至主机直接宕机了。
在分布式系统中,往往希望有多个服务器来部署redis服务,从而构成一个redis集群,此时就可以让这个集群给分布式系统中的其他服务,提供更稳定/更高效的数据存储功能 。
主要存在以下几种部署方式:
主从模式
主从+哨兵模式
集群模式
一,主从复制
1,配置主从复制
主从模式,即在若干个redis节点中,有的是主节点,有的是从节点 。
redis主从模式中,从节点上的数据,不允许修改,只允许读取数据。要想修改数据,只能访问主节点。
假设有三个物理服务器(三个节点),都部署了redis服务,此时就可以把其中一个看作是主节点,其余两个看作是从节点。从节点上的数据要跟随主节点的数据而变化,从节点的数据要和主节点保持一致。
本来,在主节点上保存了一堆数据,现在引入从节点,就是要把主节点上的数据复制出来,放到从节点中。后续,主节点这里对数据有任何修改,都会把这样的修改同步给从节点。
总结:主从模式,主要是针对读操作进行可用性和并发量的提高,而对于写操作来说,无论是可用性还是并发量,都很依赖与主节点,但是主节点又不能搞多个。
如何在一个云服务器上部署多个redis服务,按照主从模式,实现类似于分布系统的效果?(ubutun20.04)
我们可以在一个云服务器主机上,运行多个redis-server进程,我们只需保证这几个服务的端口号不同即可。本来redis-server的默认端口号是6379,此时新启动的redis-server端口号就不能是6379了。
在这里我们创建两个从节点,端口号是6380和6381,而主节点就是原先的redis-server,端口号为默认的6379。
可以通过修改配置文件来改变:
1,首先需要将配置文件拷贝两份
cp /etc/redis/redis.conf slave1.conf
cp /etc/redis/redis.conf slave2.conf
2,然后修改拷贝后的两个配置文件中的选项
修改端口号——port 6380 port6381
将该服务设置为后台进程——daemonize yes
只需修改这两个选项即可。
3,启动这两个redis-server(从节点)
redis-server slave1.conf
redis-server slave2.conf
4,现在我们只是有了多个redis-server,还没有构成主从结构。
要想配置成主从结构,就需要使用slaveof。有如下三个方法:
在配置文件中加入slaveof{masterHost} {masterPort},之后随redis启动生效。
在redis-server启动命令中加入--slaveof{masterHost}{masterPort}生效。
直接使用redis命令:slaveof{masterHost}{masterPort}生效。
其中{masterHost}和{masterPort}分别指主节点的ip和端口。
5,在这里我们选择通过修改配置文件的方式来构成主从结构
在slave1.conf和slave2.conf配置文件中加上slaveof 127.0.0.1 6379即可,以6379的redis-server作为主节点。配置文件修改完毕之后,需要我们重新启动redis-server,配置文件才会生效,所以此时需要重启端口号为6380和6381的redis-server。
由于我们启动redis-server服务时,是使用redis-server port这样的方式启动的,所以在重启的时候,需要使用kill -9来杀掉该服务进程,然后再通过redis-server port重启即可。
而如果我们是使用service redis-server start来启动服务器,就需要使用service redis-server stop来终止程序。此时如果使用kill -9来终止服务进程,kill掉之后,这个redis-server进程能自动启动 。相当于有一个进程来专门监控指定服务器的运行状态,如果服务器挂了,会立即重新启动。
因此怎么启动的redis-server,就必须搭配对应的方式进行终止。
6,再次启动redis-server即可实现主从模式
2,断开主从复制
使用命令slaveof no one可以断开主从关系,这只是暂时的,当redis-server重启之后,主从关系就会恢复,所以要想一直断开主从关系,还是需要修改配置文件。
3,拓扑
我们的redis-server服务器可能有很多个节点,如何组织这些节点?
一主一从结构
如果其他客户端想要读取数据,从主节点A或 从节点B读取都可以,两个服务器上的数据是一致的。
如果是写数据请求,那么只能由主节点A来完成。
如果此时写请求太多,也会给主节点造成 一定的压力。我们可以通过关闭主节点的AOF,毕竟写内存比写磁盘快,只打开从节点的AOF。但是这种设定,有一个严重缺陷,主节点一旦挂了,不能立即重启,因为主节点这边没有使用AOF保存数据,如果重启了,那么数据就会丢失,进一步的主从同步,也就使从节点上数据也丢失了。改进办法是,当主节点挂了,先从从节点那里获取AOF文件,再启动,这样就可以保证数据没有问题了。
一主多从结构
同理,如果是读请求,可以访问主节点,也可以访问从节点。
如果是写请求 ,是能访问主节点,主节点上的数据发生变化,就会把改变的数据同步给其他从节点。
这个结构存在的问题:同步操作是需要消耗网络带宽的,如果子节点太多,就会大大消耗主节点主机的硬件资源。
树形结构
主节点将数据同步给从节点B和从节点C,再由从节点B将数据同步给从节点D和从节点E。
此时主节点A就不需要太高的网络带宽,但此时数据同步的延时会更长。
4,原理
同步数据的命令:PSYNC replicationid offset,这个命令是从节点执行的。
其中replicationid是复制id,是主节点生成的,主节点在启动的时候就会生成。
当从节点和主节点建立了复制关系,就会从主节点上 获取到这个id,这个id就表明了当前从节点是从哪个主节点上获取的数据。
在主节点和从节点上一般都有两个replicationid:replicationid和replicationid2。其中replicationid2其备份的作用,比如下面的例子。
现在有一个主节点A和一个从节点B,A在启动的时候会生成一个replicationid,之后B获取到A的replicationid。如果A和B通信过程中出现了一些网络抖动,B可能会认为A挂了,此时从节点B就会晋升为主节点,并给自己生成一个replicationid,此时B的replicationid2就保存了之前获取到的A的replicationid。等到网络稳定,B还可以根据这个replicationid2再次与节点A建立主从关系。(但是主从复制这种方法,当主节点挂了,一般从节点是不会晋升为主节点的)
这个再次建立主从关系的过程,一般是需要手动完成的。当然,在下面的哨兵机制部分,可以自动完成这个过程。
offset表示偏移量
主节点和从节点都会维护这个偏移量(一个整数)。
主节点的偏移量:主节点可能会收到很多的修改操作的命令,每个命令都选哟占据几个字节。主节点会把这些命令的字节数进行累加,这个数字就是主节点的偏移量。
从节点的偏移量:描述了当前数据同步的进度,从节点会每秒上报自身的偏移量给主节点。
如果从节点和主节点的偏移量一致,就表述数据 同步完成了。
综上,replicationid描述了从哪个主节点同步数据,offset表示同步数据的进度。如果两个机器的replicationid和 offset一样,就表示这两个机器上的数据一致。
replicationid和offset就共同描述了一个"数据集合"。
PSYNC工作流程
PSYNC可以从主节点获取全量数据,也可以获取部分数据。
主要看offset的值,如果设为-1,表示全量获取,如果设置为具体的整数,则表示从当前整数位置来进行获取。
当然,从节点想要全量的获取数据,还是增量的获取数据,同时也取决于主节点,主节点会自行判定,看当前是否方便给部分数据,如果不方便,会直接给 全量数据。
全量复制的流程
在一个从节点与主节点第一次建立主从关系的时候,就会涉及到数据的同步,而此时一般就是进行全量复制。
在进行全量复制的时候,主节点要进行生成rdb文件的操作,然后再把该文件通过网络传输发送给从节点,从节点也是先将该rdb文件保存,然后读取该文件来获取数据。
而redis也支持"无硬盘模式"(diskless):主节点生成的rdb的二进制数据,不直接保存到文件中,而是直接通过网络传输发送给从节点 ,省去了读写硬盘的操作,而从节点现在也可以省去这个操作了,直接将收到数据加载到内存中......
但是,即使引入了"无硬盘模式",对全量复制的效率提升不是很大,因为全量复制整个操作是比较重量的,数据规模较大。相比于网络传输,读写硬盘操作算是快的了。所以减少了对硬盘的操作,但网络传输无法省去,意味着对整个操作的效率提升不大。
部分复制流程
在主节点与从节点连接的过程中,可能由于网络抖动等原因,主从节点的连接断开,此时重新建立连接并进行数据同步的时候,使用的就是部分复制。
实时复制
全量复制:主要适用于从节点刚连上主节点进行数据初始化的工作。
部分复制:是全量复制的一种特殊情况,属于全量复制的一种优化。
实时复制:在从节点与主节点建立好连接,并且完成数据同步之后,此时主节点可能会受到源源不断的请求,其中就会包含修改数据的请求,主节点的数据就会发生变化,此时就要把数据也同步给从节点。从节点和主节点之间建立有Tcp连接,当主节点收到修改数据的请求,就会通过该Tcp连接,将这个请求发送给从节点,从节点再根据这些请求修改数据即可。
所以在实时复制的时候,我们需要保证主节点与从节点的Tcp连接处于可用状态。在这里,使用心跳包机制来实现。
心跳包机制:
主节点:默认每隔10s,向从节点发送一个ping命令,从节点收到后会返回一个"pong"。
从节点:默认每隔1s,向主节点发送一个特定的请求,告诉主节点当前的同步进度(offset),之后主节点也会给出一个响应。
如果,达到某个阈值,还没有收到响应,那么就认为这个主节点/从节点存在问题,判断下线了。
5,总结
主从复制解决的问题:单点问题
单点问题:单个redis节点,可用性不高,性能有限。
主从复制的特点:
1,主节点可以用来读写,从节点只能用来读,可以减少主节点的访问压力。
2,主从复制存在多种拓扑结构:可以在适当的场景使用适当的拓扑结构,比如一主多从的结构,同步操作快,但是消耗的网络资源多,因为主节点要通过网络和所有从节点实现同步。而树形结构,主节点消耗的网络资源减少了,但是如果树的层级太高,会造成数据同步的延迟增长加。
3,复制分为全量复制,部分复制和实时复制。
4,通过心跳机制保证主节点和从节点的正常通信和数据一致。
主从复制的缺点:
1,从节点多了,数据复制的延时就会非常明显。
2,如果主节点挂了,从节点不会晋升为主节点,需要通过人工干预的方式恢复。