AKF通过对x,y,z轴的模型拆分,解决了单点故障,内存限制,访问压力的问题,但是在x轴上,主备之间数据的同步,有许多方案可以选择。如下图:
假设客户端往主redis存了一条数据,主备之间的同步有这么几种选择:
- 同步的方式,当客户端把数据给到主redis后,主redis将数据分别写入它的所有备redis,在这个写的过程中,主redis处于阻塞状态,直到所有的备redis写完,给主redis返回成功,主redis再给客户端响应成功。这种情况属于强一致性。在主redis往备redis写的过程中,如果某台备redis发生故障,那么主redis就会一直阻塞。另外,网络连接有超时的概念,所以如果备redis发生故障,那么到达一定时间后,主redis就会给客户端响应失败,从客户端的角度来讲,这就是服务已经不可用了。所以说,强一致性会破坏服务的可用性。本来集群目的就是通过解决一系列的问题来让服务可用性更强,所以强一致性这种方式一般不会用到。
- 异步的方式,当客户端把数据给到主redis后,redis写成功后,执行备份指令,然后不需要等备份成功,就直接先向客户端响应成功,然后再继续执行备份,这就保证了服务的可用性。但是响应成功后,如果在往备redis写的过程中出现故障了,那么就会丢失一部分数据。所以如果采取这种方式,那么就必须容忍丢一部分数据情况的发生。
-
使用中间件。
客户端先把数据给到主redis,主redis自身保存完毕后,把备份指令发给中间件,这个中间件可以是kafka之类的技术,这种技术要求必须数据可靠(不会丢失数据),响应速度快(能够快速给主redis作出响应),且是集群的方式(不会出现单点故障问题)。主redis把数据给到kafka之后,kafka迅速给主redis做出成功的响应,然后再往备redis里存数据。即使某个节点发生故障了,因为kafka中存有数据,所以也不会有数据丢失的情况,因为最终数据是一致的,这种特点也叫最终一致性,是弱一致性的一种。
无论以上的第2种方案还是第3种方案,在主备或者主从模式下,都无法解决一个问题。比如客户端在主redis里添加了一条数据,但是主redis还未来得及往备redis里存,这时,客户端在访问备redis时,就取不到这条数据。无论是redis,还是zookeeper,都是最终一致性的,但是可以强调为强一致性,即先去更新这条数据。这是最终一致性的一个小问题。
主备或者主从模式下,都先有个主,主又是一个单点,如果主redis出现故障了,因为客户端先是直接访问的主redis,所以其他的备用redis就没有意义了。所以这种情况下,需要对主redis作高可用。也就是主redis出现故障了,备或者从顶替上去,成为主。这个过程可以程序员手动去设置。但是人毕竟是不可靠的,所以就需要有一套自动的故障转移程序来做,也就是使用一套程序来监控主redis,如果发生故障,就进行故障转移。
但是这套程序不能是单点的,必须也得是集群的,要不然监控程序本身出现问题就没办法监控了。这套监控程序应该遵从一些规则,能够有效、准确的监控目标实例。如下图:
上图中是一个监控程序集群有三个程序监控一个redis,这种情况下,如果使用强一致性的话,也就是三个监控共同决策,这种方式无疑是最准确的,但是会出现一个问题,如果某一台监控程序出现问题了,比如断网了,那么就会影响到最终的结果。监控程序发生故障对结果的准确性影响太大,所以一般不采用。
如果每一个监控程序构成一个势力范围(也就是一个程序说了算),那么就会出现结果不一致的情况。比如监控1说是目标redis没出现问题,监控2说是目标redis出现问题了,就会对结果形成不同的网络分区。这种情况不是绝对的不能用,如果有网络分区容忍性的话,还是可以用的。
如果每两个监控程序构成一个势力范围(也就是两个程序说了算),那么另外一个是什么结果,已经不重要了,因为无论剩下的一个给出的是什么结果,都不会影响最终的结果。这种方案还是比较实用的。
由此推理,如果是4个监控程序,如果要形成一个势力范围,就需要三个“结盟”,如果是5个监控程序,也需要最少3个“结盟”。如果是n个监控程序,最少也最好需要n/2+1个“结盟”。
根据经验,一般监控集群都会使用奇数个,原因是:相对应的偶数个比奇数个更容易出现故障,而且相对应的成本更低,比如4个和3个,4个的成本更高,而且出故障的概率也更大。
其实,以上分析的就是CAP原则的思路,CAP原则就是一致性,可用性,分区容错性三个特点的结合,但是三者不可能兼顾,尤其是一致性和可用性更为明显。强一致性一定会破坏高可用性。