redis是一个单机的单进程的并且可以作为缓存和数据库来使用,它也包含了RDB和AOF持久化,
然而单机,单节点,单实例会产生各种问题,包含了单点故障【挂了就没了】,并且单机容量有
限,同时还有连接数的压力。
从而就引出了AKF,相当于从X轴Y轴进行划分
从X轴:相当于是全量镜像【不能解决容量有限的问题】
为了解决上述问题可以采用如下方式:
从X轴扩展,一台redis挂了就让另一台来代替,同时也可以做到读写分离【拿一台redis来写,另外两台
拿来读】
从Y轴扩展(针对数据容量有限的情况):每个Y轴上的redis存储不同数据【按类型或业务功能划分redis数据库】
同时还有一个Z轴:如果一个业务数据量比较大的话【如将用户划分为PC端用户和客户端
用户】,则按照优先级逻辑再拆分
但是这样的话会造成如下问题:
如果客户端对一个主redis发生了一个写,后面两个从机也要出现这个key,通过AKF实现
1变多,会产生数据一致性的问题。
解决方案:
1、通过阻塞的方式使得所有节点阻塞直到数据全部一致,强一致性【成本很高】,通过同步方式
实现。
2、但是强一致性会破坏可用性,我们使用多个redis目的就是为了解决可用性的问题,
我们需要将强一致性降级,容忍数据丢失一部分,通过异步的方式实现
异步实现的弱一致性(会丢失数据)
通过同步阻塞将数据写入到kafka(这样是足够快的)中再通过kafka往各个子redis中写数据【保证最终一致性】
在最终一致性之前的时候可能会取到不一致的数据,子redis负责的程序还没消费到这条消息。
主备:客户端只能访问主,只有主挂掉了才会访问从
主从:倾向于客户端可以访问主也可以访问从【强调主从复制,两边数据一致】
主的读写操作,右让主成了一个单点一般需要对主做HA(高可用),通过人工或者
程序来做一个自动的故障转移,所以需要一个监控程序,这个监控程序也是一个一变多的
集群。
有如上监控集群,如果三个监控里面如果有一个说redis挂了,可能是那一个
监控因为网络的原因导致判断它挂掉了,这样统计是不准确的,如果有两个
监控说redis挂了,剩余的一个监控说redis没有挂【这个势力范围更小】,
交给势力范围更大的做决策。
如果两个说redis挂了,两个说redis没挂,这样会出现脑裂,所以要解决的话需要变成3才可以做决策
数字是N的集群数那么势力数为N/2+1来解决这个集群问题。保证势力数过办,一般集群使用奇数台。
如上展示的就是一个分区容错性。
从而引出CAP原则:CAP原则又称CAP定理,指的是在一个分布式系统中,一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)。CAP 原则指的是,这三个要素最多只能同时实现两点,不可能三者兼顾。
异步弱一致性架构图实现
创建6379、6380、6381端口的三个实例
创建一个test文件并拷贝所有端口的配置文件到当前目录下
将每个conf里面的后台运行以及日志打印给注释掉
依次启动6379,6380,6381的redis服务:
然后我期望6379端口是主redis,6380和6381是从redis,
通过命令去配置从机:
redis的5.0以后是采用 replica of 的命令来实现的,5.0以前是通过slaveof来实现的
6380端口配置主机为6379端口的redis
6379端口的redis收到如下消息:
增加一个追随节点以后会增加一个同步,然后6379执行一个BGSAVE,保存一个rdb文件。
在6380的redis上面则看到连接上6379了,清除6380上的旧数据,删除完之后在加载数据到内存,保证数据都
一样的效果
现在在6379端口的redis里面写数据就能在6380对应的redis里面看到我写的数据了,不过6380作为
追随者默认是不能去写入数据的
此时再对6381端口对应的redis进行操作首先set一个k2,然后再追随6379的redis,执行完之后会发现
6381对应的k2没有了反倒多了一个k1
如果其中一个从redis挂掉了呢?
然后主redis再增加了key
我们重启一下6381,注意按如下方式去启动
发现触发了一个刷新库的操作,去连接主redis,然后从主redis同步数据,所以也能看到再挂掉的过程中
增量的数据:
发现这个启动没有造成主机RDB的事情
然后我们挂掉再重启增加一个aof。
切换到6379端口的redis发现会同步一个RDB:
原因是在于启动6381的时候会重写刷新一下数据:
redis开启了AOF是不会去碰RDB的,虽然AOF是混合模式,但是RDB里面记录了我曾经追
随的是谁,就是如下的repl-id
此时看AOF是没有那个repl-id的
所以AOF则是重新对主redis建立连接,然后刷新数据,重新将数据同步到我的从机这边来。
如果主挂掉了的话,查数据是没有问题的,然后通过人工程序将某个从变成主【故障切换】,执行replicaof no one (不追随谁)
会看到6380切换为主了
然后让6381也追随6380
配置文件配置方式
指定主机的ip及端口号
当一个副本失去与主副本的连接,或者当复制
仍然在进行中,副本可以有两种不同的方式:
如果replica- service -stale-data设置为“yes”(默认),副本将会
仍然回复客户端请求,可能使用过时的数据,或
如果这是第一次同步,# data集可能只是空的。
如果replica- service -stale-data设置为“no”,副本将会回复
对所有命令执行“SYNC with master in progress”的错误,除了:
info, replicaof, auth, ping, shutdown, replconf, role, config, subscribe,
PSUBSCRIBE
replica-read-only yes从机是否是只读的状态
repl-diskless-sync no (yes:master在内存中直接创建rdb,然后发送给slave,不会在自己本地落地磁盘了 no:则是通过磁盘IO
先落到master的磁盘上面,然后再通过网络将rdb文件发送到slave)
no的整个过程:
1.从服务器向主服务器发送SYNC命令
2.主服务器执行BGSAVE命令,生成RDB文件,并使用一个缓冲区记录从bgsave开始的所有写命令
3.主服务器BGSAVE执行完后,将RDB发送给从服务器,从服务器载入RDB文件,将自己的状态更新至主服务器的BGSAVE时的状态
4.主服务器将缓冲区的写命令发送给从服务器,从服务器执行写命令,将从服务器更新为主服务器的当前态
repl-backlog-size 1mb 做主从之间的增量复制
redis内部维护一个小的消息队列,如果master发送了一个rdb到slave上面,slave会load这个rdb文件
,如果slave短时间内挂掉了并重新启动了,那么它获取到的rdb数据可能是老数据了,
这时需要从master同步数据,此时可以通过slave的rdb里面的数据拿到一个offset,
然后再用offset(偏移量)和master里面的消息队列做比较,然后再将队列里面的数据
传到slave就好了,如果队列里面被放满了则会触发一次全量复制,根据实际业务来调整大小
注意:
1、如果是redis实现消息队列,
一般有四种方式,分别是:
使用 List 实现消息队列;
使用 ZSet 实现消息队列;
使用发布订阅者模式实现消息队列;
使用 Stream 实现消息队列。
2、master与slave的数据差异部分在repl_baklog文件,repl_baklog中会记录Redis处理过的命令日志及offset,包括master当前的offset,和slave已经拷贝到的offset;slave和master的offset之间的差异就是slave需要拷贝的数据了
3、数组一旦写满,再有新数据写入时,就会覆盖数组的旧数据。此时slave来增量同步,发现自己offset已经被覆盖了,此时只能全量同步
4、如果是slave节点第一次连接master节点时或者slave节点断开时间太久,repl_baklog中的offset已经被覆盖时,此时就全量同步,如果slave节点断开又恢复,并且在repl_baklog中能找到offset时,此时就进行增量同步
min-replicas-to-write 3
min-replicas-max-lag 10
用户设置了这两个配置选项之后,主服务器只会在从服务器的数量大于 等于min-replicas-to-write选项的值,并且这些从服务器与主服务器最后一次成功通信的间隔不超过min-replicas-max-lag选项的值时才会执行写 命令。
先按照此方式配置我们6379,6380,6381端口的redis:
我们演示的是一主二从,所以权重值我们配置为2
然后再以同样的方式配置26380和26381这两个端口的哨兵
然后再依次配置一主二从
然后再依次启动三个哨兵:
监控的master是6379,并且投票是两票
此时再增加一个26380的哨兵:
回到26379的哨兵那里,可以看到多了一条记录:
再启动26381的哨兵,这时候再让主redis挂掉,这时候两个从发现主挂掉了
这个主挂掉了一段时间以后,哨兵会监听到主redis挂掉了,然后推选出一个从机作为主机,
发现是将6381端口的redis设置成主redis了
然后再看26379.conf文件:
发现配置文件被哨兵修改了
一个哨兵是如何直到其他的哨兵的,通过发布订阅的方式,
PSUBSCRIBE 监控
所有的哨兵只知道主的信息,但是如果有从接入到master,那么哨兵和master连接过程中,可以取回从的信息
,并且通过发布订阅来发现其他哨兵。
对于监视同一个主/从服务器的多个 Sentinel 节点,他们会以每两秒一次的频率,向被监视的服务器的 sentinel:hello 频道发送消息来宣告自己存在。
Sentinel 之间不会创建订阅链接,通过命令通讯。因为已经可以通过主/从服务器获取未知的 Sentinel 服务节点。
如上只解决了从X轴扩展的单点问题,但是没有解决容量问题,如果要解决容量问题的话,只能通过程序
去控制让不同的业务访问不同的redis实例,这样来拆开redis的数据