概要
在分布式系统中,日志同步机制既要保证数据的一致性,也要保证数据的顺序性。通常简单的做法是基于Leader与Follower的机制,在集群中选取一个Leader负责数据的读取与写入顺序,Follower只需要按照Leader的持久化后的写入顺序来进行同步数据即可。
一种常见的做法是法定人数模型(Quorum),即通过少数服从多数的选举决策。在这种情况下,集群中为了能正常选举Leader,至少要有一半以上的副本节点完成日志同步并从同步完成的副本中选举新的Leader节点。法定人数模型有一个优势是写入数据的延迟取决于最快的几个节点,比如副本数为3,那么延迟取决于最快的那个follower而不是最慢的那个(除了Leader只需要一个follower确认即可)。不过其劣势就是容忍的失败节点比较少,比如要容忍1个follower失败,则至少需要3个副本,容忍2个Follower失败,则至少需要5个副本。也就是说在生产环境下保证较高的容错率,必需有大量的副本,而大量的副本又会在大数据量下导致性能急剧下降,这就是“少数服从多数”这种法定人数模型常被用作共享集群配置(比如Zookeeper),很少用于主流的数据存储的原因,因为大数据量带来的复制成本,存储成本,性能成本都有很大的挑战。
Kafka的日志同步采用类似PacificA的算法。在Kafka的Broker中动态维护了一个ISR集合,处于ISR集合中的节点保持与Leader相同高水位(HW),当前配置unclean.leader.election.enable为false(默认)时,只有ISR中的节点才能被选为Leader。写入消息时只有等到ISR集合中所有的节点都确认后才能被认为是已提交。 采用ISR模型和f+1个副本的配置下,一个Kafka最多能容忍f个节点失败,想必与少数服从多数这种方式所需要的节点大幅减少。
例如容忍1个失败副本,ISR模型最多需要2个副本。
可靠性分析
1)合理配置副本因子数量(replication.factor),正常情况需要大于3。同时分配分区时,指定机架信息,在一定程度上能抵抗宕机风险
2)客户端配置ack=all。如果ack=1,仍然无法保证可靠性,例如消息还未同步到ISR,Leader宕机,消息会都丢失。
3)对于ack=all, 只要成功写入Leader,ISR副本同步完成后告诉生产者已成功提交,则消息不会丢失,即使Leader宕机。如果写入Leader成功后,ISR副本未成功同步消息,此时Leader宕机,则生产者会收到异常,表示此次发送失败
4)客户端可配置发送重试次数
5)客户端需要关注自动提交参数(enable.auto.commit), 一般建议配置为false, 用以手动控制提交位移,避免造成消息丢失。
6) 配置最少同步副本(min.insync.replicas)。如果在某个时刻,ISR集合为空,则无法同步副本。如果Leader挂掉后,则无法保证可靠性。因此此时可配置最少同步副本数,当出现这种情况时,生产者会发送消息失败(NotEnoughReplicasException)
7)关闭不完全选举。如果把 unclean.leader.election.enable 设为 true,就是允许不同步的副本成为首领(也 就是“不完全的选举”),那么我们将面临丢失消息的风险。如果把这个参数设为 false, 就要等待原先的首领重新上线,从而降低了可用性,但是增加了消息的可靠性(不会丢消息)