🌸个人主页:https://blog.csdn.net/2301_80050796?spm=1000.2115.3001.5343
🏵️热门专栏:
🧊 Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm=1001.2014.3001.5482
🍕 Collection与数据结构 (92平均质量分)https://blog.csdn.net/2301_80050796/category_12621348.html?spm=1001.2014.3001.5482
🧀线程与网络(96平均质量分) https://blog.csdn.net/2301_80050796/category_12643370.html?spm=1001.2014.3001.5482
🍭MySql数据库(93平均质量分)https://blog.csdn.net/2301_80050796/category_12629890.html?spm=1001.2014.3001.5482
🍬算法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12676091.html?spm=1001.2014.3001.5482
🍃 Spring(97平均质量分)https://blog.csdn.net/2301_80050796/category_12724152.html?spm=1001.2014.3001.5482
🎃Redis(97平均质量分)https://blog.csdn.net/2301_80050796/category_12777129.html?spm=1001.2014.3001.5482
🐰RabbitMQ(97平均质量分) https://blog.csdn.net/2301_80050796/category_12792900.html?spm=1001.2014.3001.5482
感谢点赞与关注~~~
目录
- 1. 单点问题
- 2. 主从复制模式详细介绍
- 2.1 概述
- 2.2 配置Redis的主从结构
- 2.3 主从拓扑结构
- 2.4 主从复制原理
- 2.4.1 过程
- 2.4.2 数据同步psync
- 2.4.3 三种复制方式
- 2.5 主从复制模式存在的问题
1. 单点问题
在分布式系统中,如果某个服务器程序,只有一个结点,也就是只有一个服务器程序来部署这个服务器程序.这时候就会出现一个非常关键的问题,叫做单点问题.
- 首先会出现可用性问题,如果这个服务器挂了,那么就意味着服务就会中断.
- 其次是性能问题,只有一台服务器的时候,所支持的并发量也是有一定限制的.不可以支持数量较大的客户端来访问.
所以我们就可以引入分布式系统来解决上述单点问题.
在分布式系统中往往有多个服务器来部署Redis服务,从而构成一个Redis服务的集群.此时就可以容这个集群给整个分布式系统中的其他服务提供更加高效而又稳定的数据存储功能的服务.
在分布式系统中,Redis的部署方式通常有以下的三种方式:
- 主从复制模式
- 主从哨兵模式
- 集群模式
2. 主从复制模式详细介绍
2.1 概述
主从主从,顾名思义就是有的服务式"主"结点.有的是"从"结点.比如我们有三台物理服务器(称为三个节点),此时就可以把其中一个节点,作为"主节点",而另外两个作为"从节点".这时候,从结点就必须听主节点的,从结点的数据要跟随主节点变化,从结点的数据需要和主节点保持一致.也就是主节点原有的数据,在与从结点建立连接之后,就需要把主节点上面的数据复制到从结点上,后续主结点这边对于数据有任何修改,都会把这样的修改也同步给从结点上.说的再简单一点,从结点就是主节点的一个副本.
- 那么如果修改了从结点上的数据,会不会往主节点上同步呢?
Redis主从模式中,从结点上的数据是不允许修改的,从节点上的数据是只读模式,只允许读取,不允许修改,增加,删除.
这也就是说,主从模式是针对"读操作"进行并发量和可用性的提高.而写操作无论是可用性还是并发,都非常依赖主节点,而实际业务中,读操作就是比写操作更加频繁.如果挂掉了一个从结点,其实没有多大影响,此时继续从主节点或者是其他结点上读取数据,得到的效果是完全相同的,但是如果主节点挂掉,还是有一定影响的,从节点只能读取数据,如果需要写数据,就没得写了.
- 上述这种主从结构,便解决了单体结构最主要的两个问题,一个就是保证了Redis服务的可用性,通过增加Redis服务器的节点,来保证Redis服务的可用性,这些服务不可能同时崩溃.其次是增加了Redis服务的并发量,由于从结点中的数据都是时刻和主节点中保持一致的,因此后续有客户端来读取数据的时候,就可以主节点和众多从结点中随机挑选一个结点,来给这个客户端提供读取数据的服务.引入了更多的计算机资源,自然可以支撑更高的并发量.
2.2 配置Redis的主从结构
想要搭建一个Redis的主从结构,我们需要启动多个Redis服务器,正常来说,每个Redis服务器程序,应该在一个单独的机器上,但是如果说现在我们手里只有一台云服务器的话,我们也是可以在一个云服务器上去运行多个Redis-Server进程的,只不过我们必须要保证多个Redis服务的端口号都是不一样的,比如我们之前Redis-服务的默认端口是6379,之后启动另一个Redis服务就不能是6379了,一个服务可以有不同的端口,但是一个端口不可以启动多个服务.我们接下来就在云服务器上通过实际操作来完成多个Redis-Server的启动.
- 首先我们需要有多份Redis的配置文件.
root@iZ2ze9pwr3i8b65w9dr55dZ:~# cd /etc/redis
root@iZ2ze9pwr3i8b65w9dr55dZ:/etc/redis# ls
redis.conf
root@iZ2ze9pwr3i8b65w9dr55dZ:/etc/redis# cp redis.conf redis-slave1.conf
root@iZ2ze9pwr3i8b65w9dr55dZ:/etc/redis# ls
redis.conf redis-slave1.conf
root@iZ2ze9pwr3i8b65w9dr55dZ:/etc/redis# cp redis.conf redis-slave2.conf
root@iZ2ze9pwr3i8b65w9dr55dZ:/etc/redis# ls
redis.conf redis-slave1.conf redis-slave2.conf
- 其次我们需要在复制出来的这两份配置文件中修改他们的持久化文件的文件名和端口.
port 6380
dbfilename dump-slave1.rdb
appendfilename "appendonly-slave1.aof"
port 6381
dbfilename dump-slave2.rdb
appendfilename "appendonly-slave2.aof"
- 其次我们需要通过`redis-server 配置文件路径``来启动这Redis结点.启动之后我们可以通过ps命令来查看Redis服务是否启动成功.
root@iZ2ze9pwr3i8b65w9dr55dZ:/etc/redis# redis-server ./redis-slave1.conf
root@iZ2ze9pwr3i8b65w9dr55dZ:/etc/redis# redis-server ./redis-slave2.conf
root@iZ2ze9pwr3i8b65w9dr55dZ:/etc/redis# ps -aux|grep redis
redis 9622 0.1 0.3 77456 6256 ? Ssl Oct26 23:28 /usr/bin/redis-server 0.0.0.0:6379
root 637750 0.1 0.3 67212 5956 ? Ssl 20:42 0:00 redis-server 0.0.0.0:6381
root 637760 0.1 0.3 67212 5976 ? Ssl 20:42 0:00 redis-server 0.0.0.0:6380
root 637804 0.0 0.1 6480 2400 pts/0 S+ 20:43 0:00 grep --color=auto redis
上述的操作只是启动了多个Redis服务,这几个Redis服务之间并没有形成主从结构.此时我们还需要在配置文件中添加主节点的Host和port.slaveof masterHost masterHost
.
slaveof 127.0.0.1 6379
当然我们也可以在启动Redis服务的时候加入从结点
redis-server ./redis-slave1.conf --slaveof 127.0.0.1 6379
,或者是在Redis客户端中输入slaveof 127.0.0.1 6379
与主节点建立连接.但是这些连接只是暂时性的,在服务重启之后,主从节点之间又会断开,并按照配置文件中的配置重新加载.
- 修改完配置文件之后重新启动从结点服务.
root@iZ2ze9pwr3i8b65w9dr55dZ:/etc/redis# ps -aux | grep redis
redis 9622 0.1 0.3 77456 6256 ? Ssl Oct26 23:29 /usr/bin/redis-server 0.0.0.0:6379
root 637750 0.1 0.3 67212 5944 ? Ssl 20:42 0:00 redis-server 0.0.0.0:6381
root 637760 0.1 0.3 67212 5964 ? Ssl 20:42 0:00 redis-server 0.0.0.0:6380
root 638277 0.0 0.1 6480 2448 pts/0 S+ 20:54 0:00 grep --color=auto redis
root@iZ2ze9pwr3i8b65w9dr55dZ:/etc/redis# kill -9 637750
root@iZ2ze9pwr3i8b65w9dr55dZ:/etc/redis# kill -9 637760
root@iZ2ze9pwr3i8b65w9dr55dZ:/etc/redis# ps -aux | grep redis
redis 9622 0.1 0.3 77456 6256 ? Ssl Oct26 23:29 /usr/bin/redis-server 0.0.0.0:6379
root 638313 0.0 0.1 6480 2292 pts/0 S+ 20:54 0:00 grep --color=auto redis
root@iZ2ze9pwr3i8b65w9dr55dZ:/etc/redis# redis-server /etc/redis/redis-slave1.conf
root@iZ2ze9pwr3i8b65w9dr55dZ:/etc/redis# redis-server /etc/redis/redis-slave2.conf
注意:kill -9这种命令和我们之前使用redis-server命令来启动Redis服务是配套的.如果使用的是service redis-server stop来停止.如果使用kill -9这种方式来停止的话,kill掉之后,这个Redis进程就会自动再启动起来.
- 此时如果在主节点中进行数据的输入之后,从结点就会和主节点中的数据进行同步.我们通过在redis-cli后面加上
-p 端口号
来启动对应端口的Redis.
root@iZ2ze9pwr3i8b65w9dr55dZ:/etc/redis# redis-cli -p 6379
127.0.0.1:6379> SELECT 2
OK
127.0.0.1:6379[2]> keys *
1) "key3"
2) "key2"
3) "key1"
127.0.0.1:6379[2]> FLUSHDB
OK
127.0.0.1:6379[2]> set key1 value 1
(error) ERR syntax error
127.0.0.1:6379[2]> set key1 value1
OK
127.0.0.1:6379[2]> set key2 value2
OK
我们启动从结点,发现主节点的数据已经同步到了从节点.
root@iZ2ze9pwr3i8b65w9dr55dZ:~# redis-cli -p 6380
127.0.0.1:6380> SELECT 2
OK
127.0.0.1:6380[2]> keys *
1) "key2"
2) "key1"
root@iZ2ze9pwr3i8b65w9dr55dZ:~# redis-cli -p 6381
127.0.0.1:6380> SELECT 2
OK
127.0.0.1:6380[2]> keys *
1) "key2"
2) "key1"
- 此时我们可以通过查看redis-server之间的TCP网络连接来查看redis-server之间的连接.
netstat -anp|grep redis
经过分析,我们看到,redis的主从节点之间已经成功建立了网络连接.
在主节点和从结点的TCP网络连接内部,支持了nagle算法,他是默认开启的.开启了,就会增加TCP的传输延迟,但却节省了网络的带宽.要是关闭,则相反,会减少TCP的传输延迟,但却增加了网络延迟的带宽.这里的目的和TCP中的捎带应答是一样的,针对较小的TCP数据报进行合并,以减少数据报的传输个数.可以使用
repl-disable-tcp-nodelay
这个选项来关闭TCP中的nagle算法,值可以为no或者是yes,默认为no.
- 我们也可以通过redis中的
info replication
指令来查看主从节点的连接情况.
主节点:
其中master_replid
代表的是主节点的身份标识.master_replid2
之所以全是0,是因为该id还没有用到,这个id在网络抖动的情况下会启用,如果网络出现了抖动,从结点会认为主节点宕机,从结点就会自己成为主节点,但是此时该从结点还会记得原来旧的主节点的replid,就是通过replid2来记录,后续网络稳定了,B还可以根据replid2重新与原来的主节点进行连接.offset=111004
代表的是复制的偏移量.表示当前从节点复制到了哪里,是一个位置标识.以repl_backlog
开头的四个字段表示的是积压缓冲区,积压缓冲区用于支持部分同步机制的实现.
从节点:
以master
开头的字段代表的是主节点的信息.slave
开头的代表的是当前从节点的信息.connected_slaves
代表的是当前节点之下的的从结点.如果没有,显示0.
我们可以在从节点中输入
slaveof no one
指令用来断开现有的所有的主从复制关系.从结点断开主从关系之后,就不在从属于其他的节点了,但是里面已有的数据不会被抛弃,当前断开的从结点会晋升为主节点.
2.3 主从拓扑结构
所谓的拓扑结构指的就是若干个节点按照什么样的方式来组织连接.Redis的复制拓扑结构可以支持单层或者多层的复制关系,根据拓扑复杂性可以分为以下三种:一主一从、一主多从、树状主从结构.
- 一主一从拓扑
这是一种最简单的拓扑结构.这种结构在写数据的请求太多的时候,会给主节点造成一定的压力.可以通过关闭主节点的AOF,只在从结点上开启AOF,一减少主节点的压力.但是这种设定存在一种严重的缺陷,一旦主节点宕机,不可以让主节点重启,一旦重启,由于主节点没有持久化文件,数据就会丢失,进一步的主从同步就会把从结点的数据也给删除.改进办法就是,每次主节点挂了之后,就要让主节点从从结点这里获取AOF文件,再重新启动. - 一主多从
这种结构对于读数据比重较大的场景,可以把读命令负载均衡到不同的从结点上来分担压力.但是缺点就是这种结构不适用于写数据比重较大的场景,由于主节点连接的从结点较多,随着从结点个数的增加,同步一条数据,就需要传输多次,这会大大增加主节点的网络传输压力. - 树形拓扑
这种结构使得从节点不但可以复制主节点数据,同时可以作为其他从节点的主节点继续向下层复制.这种结构相比上一种拓扑结构可以减少主节点网络传输的压力,不需要那么高的网卡带宽,一旦数据进行修改了,同步的延时比刚才更长.
2.4 主从复制原理
2.4.1 过程
首先保存主节点的信息,包括ip和端口.之后建立主从节点之间的网络连接.也就是TCP连接(会经历三次握手的过程).之后给主节点发送ping命令.验证主节点是否可以正常工作.之后就是同步数据集(全量同步,首次连接同步所有数据)和命令持续复制(增量同步,后续每一步操作都进行同步)这两个最关键的复制数据的操作了.
2.4.2 数据同步psync
Redis提供了psync命令来完成数据同步的过程.psync指令一般不需要我们手动执行,Redis服务器会在建立好主从同步关系之后,自动执行psync.他的语法格式是PSYNC replicationid offset
.
- replicationid
表示的是主节点的复制id.这个id是主节点启动的时候就会生成,每次主节点重新启动生成的reolicationid都是不一样的.或者是从节点和主节点断开之后,从结点晋升为主节点的时候,也会生成.这个参数其实就是我们之前通过info replication
查询出来的master_replid
.从结点和主节点建立的复制关系之后,就会从主节点这边获取到replicationid. - offset
这个参数是主节点和从结点上都会维护的数据偏移量,他是一个整数.
主节点上会收到很多很多的修改操作的命令,每个命令都要占据一定的字节数,主节点会把这些修改命令中的数据的字节数进行累加,得到主节点的offset.
从结点的偏移量,描述的是,现在的从结点这里在从主节点同步数据的时候同步到了哪里.从结点每秒钟都会上报自身的复制偏移量给主节点. - psync这里,可以从主节点中获取全量数据,也可以获取一部分数据主要看的就是offset这里的进度.**offset如果写作-1,就是获取全量数据,如果offset写作具体的整数,则是从当前的偏移量来进行获取.
- 这里在获取数据的时候也不是从节点所要哪部分,主节点就给哪部分,主节点会自行进行判定,看当前是否方便给部分数据,不方便就只能给全量数据了.
如果主节点回复的是+FULLRESYNC replid offset,则从节点需要进行全量复制流程.
如果回复+CONTINEU,从节点进行部分复制流程.如果回复-ERR,说明Redis主节点版本过低,不支持psync命令.
2.4.3 三种复制方式
- 全量复制
我们通过前面的学习可以知道,在首次和主节点进行数据同步的时候,在主节点不方便进行部分复制的时候,从结点就会进行全量复制.下面是全量复制的流程.此时的replid和offset分别是?和-1.
- 从节点发送psync命令给主节点进行数据同步,由于是第一个进行复制,从结点没有主节点的运行ID和offset,所以是全量复制.
- 主节点根据命令,解析出要进行全量复制,回复+FULLRESYNC响应.
- 从结点接受主节点的运行信息进行保存.
- 主节点执行bgsave进行RDB文件的持久化.
- 从结点发送RDB文件给从结点,从节点保存RDB文件到本地磁盘.
- 主节点将从生成RDB文件到接收完成期间执行的写命令的数据,写入缓冲区,等从节点保存完RDB文件之后,主节点在将缓冲区中,等从结点保存完RDB文件之后,主节点在将缓冲区内的数据补发.
- 从节点清空自身原有的旧数据.
- 从节点加载RDB文件得到与主节点一致的数据.
- 如果从节点开启了AOF持久化,会进行重写操作,如果没有开启,数据全量化复制完成.
主节点在进行全量复制的时候,也支持"无硬盘模式".主节点生成的RDB二进制数据,不是直接保存到文件中了,而是直接进行网络传输,从节点之前,也是把收到的RDB数据写入到硬盘中,然后加载.现在也可以省略这个过程,直接把收到的数据进行加载了.
虽然这样的操作节省了读写硬盘的操作,但是网络传输的消耗是没办法省略的.
- 部分复制
从节点要从主节点这里进行全量复制开销是很大的,有些时候,从节点本身已经持有了主节点的绝大部分的数据,这时候,就不太需要进行全量复制了.
比如出现了网络抖动,主节点的数据没有来得及同步过来,当网络回复稳定的时候,此时就可以让从结点和主节点重新建立连接,此时就需要进行部分数据的同步.
psync要是带有具体的replid和offset的值.主节点就要根据psync的参数进行判定,当前这次是按照全量复制合适还是部分复制合适.
1. 当主节点和从结点出现网路中断的时候,如果超过repl-timeout时间,主节点就会认为从结点故障.
2. 主从节点中断期间,主节点依然会响应命令,但是这些复制都因为网络中断无法及时发送给从结点,所以暂时将这些命令滞留在积压缓冲区.
3. 主节点与从结点网络恢复.
4. 从节点将之前保存的replicationid和复制偏移量作为psync的参数发送给主节点,请求进行部分复制.
5. 主节点接收到psync请求之后,进行必要的验证,随后根据offset区复制积压缓冲区查找合适的数据,并响应+CONTINUE给从节点.
6. 主节点将从结点需要同步的数据发送给从结点,最终完成一致性.
在第5步的时候,验证的步骤具体是什么呢?首先验证replicationid和之前的是不是一样的.如果步一样,那么就会对数据进行全量复制.如果replication一样,在判断offset,看看当前offset是否在积压缓冲区中存在,如果存在就可以把数据部分复制过去,如果不存在,就需要全量复制.
- 实时复制
从结点已经和主节点同步好了数据,但是之后主节点这边会源源不断的接收到新的修改数据的请求.主节点上的数据就会随之改变,这些数据也能够要同步给从结点.
从结点和主节点之间会建立TCP长连接,然后主节点把自己收到的修改数据的请求通过上述的连接发送给从结点.
在进行实时复制的时候,需要保证网络连接是可用的状态.保证网络可用,TCP就引入了心跳包机制,主节点默认每隔10s给从结点发送一个ping命令,从结点接受到之后返回pong.从结点默认每1s就会给主节点发起一个特定的请求,上报当前从节点复制数据的进度(offset).
2.5 主从复制模式存在的问题
最大的问题还是在主节点上,如果主结点挂了,从节点就会迷茫,虽然能够提供读数据的操作,但是如果主节点意外宕机,从结点不会自动升级为主节点(除非是通过slave no one断开),此时就需要程序员手动恢复主节点.此时就需要引入哨兵模式来解决问题了.