Redis主从复制
文章目录
Redis的数据一致性
单机Redis会带来哪些问题
- 单点故障问题
- 容量有限问题
- (IO、CPU)压力
利用AKF理论解决上述的问题,分别从XYZ三个轴解决问题。
X轴:是进行全量的复制,相当于镜像的功能,解决了单点故障,并分担了主机压力。
Y轴:安装业务、功能进行区分。解决了容量的问题。
Z轴:按照优先级再拆分。如果按照Y轴redis的容量还不够,那么再进行全量的复制,也就是对业务的redis执行全量的镜像功能。
但是,通过AKF理论将一台变多台redis,但是却没有关注到数据一致性问题,如果将所有的节点都阻塞,直到数据全部一致,这会破坏可用性,因为此时数据为强一致性。所以如果要强一致性,那么一定会破坏redis的可用性。那么怎么解决这个问题呢?
首先讨论为什么需要一变多,这是为了解决单点故障,保证可用性。但是如果用了强一致性会破坏此时的强一致性,那么是否有可行的方案?
可以将同步阻塞的方式改成异步阻塞的方式,不能容忍数据的丢失改为可以容忍数据丢失一部分。client传入k1键值,给了主机,主机再给从机,等待从机返回ok,再返回client的ok信号,在这个中途中,redis主机会阻塞等待写成功。为了解决这个问题,client只要给了redis主机,主机立马返回ok,但是此时假设从机挂了或者返回失败,那么就会丢失数据。(弱一致性)
第三种解决数据不一致的方案为最终一致性,只要保证数据最终是一致的,这个时候,数据会从redis主机写进kafka,写完后reids再返回client,这个过程中时间是很少的,从机可以慢慢的从kafka中拿数据。但是有可能会产生拿数据不一致的现象。redis集群是用的弱一致性,redis要求快,减少技术的整合,不能抹杀掉redis的快。
Redis集群
1.redis高可用(x轴)
虽然在一变多的时候,解决了数据一致性的问题,但是又有个问题,我们知道,一变多一般都是用主从或者主备的方案,一般是用主从方案较多。
主备:访问在主机,挂掉之后,才会用到备机
主从:访问可以在从机。
但是用主从的时候,主又是个单点,发现怎么单点故障没有解决?所以这里需要对主做高可用了,备作主。高可用是对主做的。主的挂了,为了不出问题,把备的当作主顶上去,从外面来看,一直是有主的。
需要做高可用,我们想怎么去监控主是否挂掉呢?
我们一般利用程序的方式对主进行监控,但是一个程序如果又出现了问题,也会存在单点故障问题,所以程序本身必须为一个集群,这里用到了分区容忍性(可以容忍数据的不一致。10台tomcat,接受多并发,挂了一个,但是只需要一台可用就行)。
分区容忍性的引入是为了解决脑裂的问题,如果1个监控说主机挂了,另外一个监控说没有挂,那么会出现脑裂的问题,原因很多,有可能是网络延迟或者是监控节点挂了。
分区容忍性表示不一定需要所有的机子都同意,只需要一般以上,一般是(n/2)+1台监控组成势力范围,决定主机是否挂了。
2个人结为势力范围,还有一个成为实力范围。若给出4个监控,那么这时2不够用,用3来成为势力范围。5个监控。那么3个也可以。可以决策。
数字是n/2 + 1时,能解决该问题。
一般使用奇数台!4台比3台更容易出现故障。所以一般用奇数台。
CAP原则:一致性、可用性与分区容忍性不可都满足redis一般是满足可用性与分区容忍性。一般 CAP只有CP或者AP。redis的可用性为最终一致性
下面讨论下根据以上的知识如何在redis中实现。
先搭建redis主从集群,再用REPLICAOF去追随主。
也可以从上图看到准备RBSAVE做同步,并落到磁盘上。
主要通过RDB同步数据,从机需要先把自己老数据删除,删除之后再把新数据加载到自己的内存。从机默认情况下是禁止写入的。
从下图可以看到都会有RDB的文件,这里是master传递过来的RDB文件。
但是如果在从机挂了之后,又写了新数据,是增量的写入到从机,还是又落了一个RDB文件呢?
是增量同步的过程,不会触发RDB(但是必须是以前同步过的,直接给出偏移量,让从机拿走就行)
也可以指定开启AOF文件
开启AOF了过后,从机的每一次的挂了重启,必须会触发RDB的事情。也就是全量同步。
以上是从机挂的情况,但是如果主机挂了呢?
凡是主,会知道一件事情,知道从机是哪台。如果没有哨兵,需要自己设置主,如下图所示。
再另外一台需要追随新的主。
redis先给磁盘落一个RDB,走完之后,走网络IO给另外一个redis。其实也可以将RDB从网络中传输。
下面是redis配置的主要部分。
在配置中,replica-serve-state-data yes 代表备机需不要把老的数据暴露,或者是等同步完成后一起暴露新数据。
replica-read-only 备机是否只支持查询
repl-diskless-sync no no是走磁盘,yes是走网络IO。
repl-backlog-size 1mb 主和从之间有个增量复制 。主通过RDB给到了从的全量数据,主redis内部维护小的消息队列,如果从redis短时间内挂了又活了,主给从一个偏移量,8-20传过去,但是如果数据写入消息队列太多,会挤掉数据,这个时候会触发全量的复制。需要调整大小。
min-relicas-to-write 3
min-replicas-max-log 10这两个配置项,规定最小写几个成功(向强一致性靠拢)
2.redis的哨兵
这样的主从复制有个问题,需要人工维护主的故障问题,这才需要HA高可用哨兵。
,哨兵需要3个任务,监控,提醒与自动的故障转移。写这样的配置文件,各三个。
最后的2为投票的个数。
一套哨兵可以监控多个主从集群,这里的mymaster只是这套集群的逻辑名字。
把三个redis实例跑起来,再启动哨兵,哨兵也是通过redis-server方式启动的。
但是从上图的哨兵程序可以看出,他能监控出几个从。怎么发现从的呢?通过发布订阅的方式实现的,哨兵会向主库发送 INFO 命令,主库就会把从库列表返回给哨兵。在配置中没有写从机的地址,如果又加入了个哨兵,也会通过发布订阅的方式知道另外一个哨兵。sentinel通过发送HELLO信息让其他sentinel发现,与此同时,sentinel通过订阅连接接受其他sentinel的HELLO信息。
sdown是主观下线,odown是客观下线。
具体过程是这样的:
1.多个sentinel发现并确认master有问题。
2.选举出一个sentinel作为领导
3.选出一个slave作为master(从库优先级、从库复制进度以及从库ID号)
1、根据从库优先级打分 从库上可以根据 slave-priority 配置项,配置该从库的优先级。 这个优先级的设置,可以根据实例本身的机器内存、磁盘、CPU配置设置这个优先级。
2、根据旧主库同步程度最接近的从库打分
主库同步数据给从库的时候,会带有一个 offset 标记同步进度。 实际上,只需要比较各个从库的
offset,这个数据越大,说明越接近主库数据,那么在这一轮打分越高。
3、ID号小的从库得分高
这个实际上就是一个托底逻辑,每个实例都会有一个ID,在前两轮都无法确定出主库的情况下,ID 号最小的从库最分最高。
4.通知其余slave成为新的master的slave
主从库的切换,是由哪个哨兵完成的呢?
由哪个哨兵的完成,也需要哨兵集群内部进行选举,这个时候会小其他哨兵发送is-master-down-by-addr命令,其他哨兵会返回Y与N,获取得到一定的Y后,就是leader。
如果主挂了之后,从机会在隔了一段时间发现后,投票选出一个leader,投票的过程是根据优先级确定的,优先级越高,则成为leader。
但是哨兵集群有个问题,哨兵选举一个从节点成为主节点,在这期间客户端来访问是被阻塞的,因为主节点正在被选举。
集群模式和哨兵模式的区别
哨兵模式监控权交给了哨兵系统,集群模式中是工作节点自己做监控
哨兵模式发起选举是选举一个leader哨兵节点来处理故障转移,集群模式是在从节点中选举一个新的主节点,来处理故障的转移。
3.如何解决容量有限问题(Y轴)
哨兵模式解决了单点故障,但是没有解决容量有限的问题,也就是Y 与 Z轴需要解决。
一般让redis内存占几个G,而不是全部占满,让redis更轻盈。
如果大量数据过来,怎么放进redis?
有四种方法:
第一种方案:
对客户端方向,在Y轴上,根据不同业务划分到不同的Redis中去。
第二种方案:
假设根据业务拆分,数据还是很大,一个redis放不下,这个时候需要结合算法,对每一个key,hash + 取模,从这个方案开始,进入sharding分片,但是有个弊端,hash取模算法的模数值是固定的,mod 3或者mod 5 的结果不一样,会影响分布式下的扩展性(不能再加机器了)。
第三种方案:
随机方式给不同的redis,弊端:但是自己有可能找不到数据在哪。
Random随机性比较大,不知道数据放在哪儿了,但是又这种场景,比如ooxx的key,通常用list类型,另外一个客户端只要消费就行。一般拿来做消息队列。Kafka基于磁盘的,可以重消费,redis的random模式是基于内存的。
第四种方案:
为了解决第二种方案的弊端,用一致性hash算法。
每个redis节点给他唯一的号,经过 hash算法,会产生一个映射值,这个映射值一定在hash环的某一个点上。如果来了数据,用key来计算hash值时,由于环上的点是虚拟的,但是node节点映射的点是物理的。这个时候用排序集合比如treemap把这些点放到treemap上,算出key(data)的值的时候,可以从treemap中找,凡是大过key的点的物理点有哪些,有可能后边有几个物理节点,找到最近的哪个物理点,把值通过这个点代表的物理机存过去就行。
如果此时加入了node03,在data与node02之间。数据只能在node03上存储,但这个时候有个问题,在data与node03之间的数据会找不到了。(不影响后面的查找,但是影响前面的查找)新增的节点一小部分不能命中,会击穿,压给了数据库。
如何解决?每次取理data最近的两个节点。(复杂度过高,需要取舍)
如果将一个物理节点参与10次hash值计算,映射到环上,2台有20个点。可以解决数据倾斜问题。
4.redis的集群
4.1 redis集群的代理实现(tw以及predixy)
但是,以上三种方案有个问题,这里只有一个客户端,但是在生产环境下,会有很多个客户端,这个时候对server造成分redis的连接成本很高,怎么解决这个问题?可以用代理的方式。
用一个代理的时候,会管制代理的性能,此时会出现一个代理不够用,用两台代理。如果代理出现了问题,加一个LVS。此时不用对proxy做高可用了,因为LVS不可能是一台,LVS做个主备,用keepalived关联。keepalived可以监控代理的状态、切换LVS的主备。在这种代理模式的情况下,会将在客户端的三种逻辑实现迁移到代理层实现。
代理模式有几种,比如twemproxy,内部怎么用散列的呢?比如可以采用hash、CRC等。
4.2 redis集群cluster
在一致性hash算法中,由于数据会丢失,在hash算法中,由于取模会变动,所以受限于节点数量上,所以这几种方案不适合做数据库用。
怎么解决?预分区。让取模取的足够大,新增物理节点数会小于取模数量。如果是10个槽位,2台物理机,每台有5个,如果新加了个节点,此时会将一部分槽位给新加的节点。
这里的数据一致性也是通过时点再加缓存加进去的。
这种非模式下,没有主。客户端想连谁就连谁。redis除了有取模的功能,在与槽位进行匹配,每个redis实例都会有映射关系,再给客户端返回位置,最后到正确的redis中去。以下是取k1的值过程。
但是这样的数据分治有个问题,聚合操作很难实现,如何解决?
使用00进行取模,而不是用k1与k2.自己把数据移到一起。
从官方文档上可以看出,一共有16384个槽位。