1.ack机制

request.required.acks参数表示的是生产者生产消息时,写入到副本的严格程度,决定了生产者如何在性能和可靠性之间做取舍。

acks参数有三个值 :

acks为1时(默认),表示数据发送到Kafka后,经过leader成功接收消息的的确认,才算发送成功,如果leader宕机了,就会丢失数据。
acks为0时, 表示生产者将数据发送出去就不管了,不等待任何返回。这种情况下数据传输效率最高,但是数据可靠性最低,当 server挂掉的时候就会丢数据;
acks为-1/all时,表示生产者需要等待ISR中的所有follower都确认接收到数据后才算发送完成,这样数据不会丢失,因此可靠性最高,性能最低。


2.副本的同步机制


同步副本时,follower获取leader的LogStartOffest(起始位置)和LEO(下一条待写入的位置),与本地的进行比较,如果本地的LogStartOffest超出了leader的值,就把超出的数据删除掉在进行同步,如果本地的小于leader的,就直接同步。

复制系数

主题级别的配置参数是 replication.factor,而在 broker 级别则可以通过 default. replication.factor 来配置自动创建的主题。

Kafka 的默认复制系数就是 3,不过用户可以修改它。

如果复制系数为 N,那么在凡 l 个 broker 失效的情况下,仍然能够从主题读取数据或向主题写入数据。所以,更高的复制系数会带来更高的可用性、 可靠性和更少的故障。另一方面,复制系数 N 需要至少 N 个 broker ,而且会有 N 个数据副本,也就是说它们会占用 N 倍的磁盘空间。我们一般会在可 用性和存储硬件之间作出权衡。 那么该如何确定一个主题需要几个副本呢?这要看主题的重要程度,以及你愿意付出多少成本来换取可用性。。 如果因 broker 重启导致的主题不可用是可接受的(这在集群里是很正常的行为),那么把复制系数设为 1 就可以了。在作出这个权衡的时候,要确 保这样不会对你的组织和用户造成影响,因为你在节省了硬件成本的同时也降低了可用性。复制系数为 2 意味着可以容忍 1 个 broker 发生失效,看起来 已经足够了。不过要记住,有时候 1 个 broker 发生失效会导致集群不稳定(通常是旧版的 Kafka ),迫使你重启另一个 broker-一集群控制器。也就是 说,如果将复制系数设为 2 ,就有可能因为重启等问题导致集群不可用。 基于以上几点原因,在要求可用性的场景里把复制系数设为 3 。在大多数情况下,这已经足够安全了,不过要求更可靠时,可以设为更高,比如我 5 个副本,以防不测。 副本的分布也很重要。默认情况下, Kafka 会确保分区的每个副本被放在不同的 broker 上。不过,有时候这样仍然不够安全。如果这些 broker 处 于同一个机架上, 一旦机架的交换机发生故障,分区就会不可用,这时候把复制系数设为多少都不管用。为了避免机架级别的故障,我们建议把 broker 分 布在多个不同的机架上。 不完全的首领选举 unclean.leader.election 只能在 broker 级别(实际上是在集群范围内)进行配置, 它的默认值是 true。当分区首领不可用时, 一个同步副本会被选为新首领。如果在选举过程中没有丢失数据,也就是说提交的数据同时存在于所有的同步副本上,那么 这个选举就是“完全”的。 但如果在首领不可用时其他副本都是不同步的,我们该怎么办呢? 这种情况会在以下两种场景里出现。 • 分区有 3 个副本,其中的两个跟随者副本不可用(比如有两个 broker 发生崩愤)。这个时候,如果生产者继续往首领写入数据,所有消息都会得 到确认井被提交(因为此时首领是唯一的同步副本)。现在我们假设首领也不可用了(又一个 broker 发生崩愤),这个时候,如果之前的一个跟随者重 新启动,它就成为了分区的唯一不同步副本。 • 分区有 3 个副本,因为网络问题导致两个跟随者副本复制消息滞后,所以尽管它们还在复制消息,但已经不同步了。首领作为唯一的同步副本继 续接收消息。这个时候,如果首领变为不可用,另外两个副本就再也无法变成同步的了。 对于这两种场景,我们要作出一个两难的选择。 如果不同步的副本不能被提升为新首领,那么分区在旧首领(最后一个同步副本)恢复之前是不可用的。有时候这种状态会持续数小时(比如更换 内存芯片)。 ·如果不同步的副本可以被提升为新首领,那么在这个副本变为不同步之后写入旧首领的消息、会全部丢失,导致数据不一致。为什么会这样呢? 假设在副本 0 和副本 1 不可用时,偏移量 100-200 的消息被写入副本 2 (首领)。现在副本 2 变为不可用的,而副本 0 变为可用的。副本 0 只包含偏 移量 0~ 100 的消息,不包含偏移量 100~ 200 的悄息。如果我们允许副本 0 成为新首领,生产者就可以继续写人数据,消费者可以继续读取数据。于 是,新首领就有了偏移量 100 ~200 的新消息。这样,部分消费者会读取到偏移量 100 ~200 的旧消息,部分消费者会读取到偏移量 100~200 的新消 息,还有部分消费者读取的是二者的混合。这样会导致非常不好的结果,比如生成不准确的报表。另外,副本 2 可能会重新变为可用,并成为新首领的 跟随者。这个时候,它会把比当前首领旧的消息全部删除,而这些消息对于所有消费者来说都是不可用的。 简而言之,如果我们允许不同步的副本成为首领,那么就要承担丢失数据和出现数据不一致的风险。如果不允许它们成为首领,那么就要接受较低 的可用性,因为我们必须等待原先的首领恢复到可用状态。 如果把 unclean.leader.election 设为 true ,就是允许不同步的副本成为首领(也就是“ 不完全的选举”),那么我们将面临丢失消息的风险。如果 把这个参数设为 false ,就要等待原先的首领重新上线,从而降低了可用性。我们经常看到一些对数据质量和数据一致性要求较高的系统会禁用这种不完全的首领选举( 把这个参数设为 false ) 。比如银行系统,大部分银行 系统宁愿选择在几分钟甚至几个小时内不处理信用卡支付事务,也不会冒险处理错误的消息。不过在对可用性要求较高的系统里,比如实时点击流分析 系统, 一般会启用不完全的首领选举

 

最少同步副本

在主题级别和 broker 级别上,这个参数都叫 min.insync.replicas。

我们知道,尽管为一个主题配置了 3 个副本,还是会出现只有一个同步副本的情况( acks=0 或 1)。如果这个同步副本变为不可用,我们必须在可 用性和一致性之间作出选择---这又是一个两难的选择。根据 Kafka 对可靠性保证的定义,消息只有在被写入到所有同步副本之后才被认为是已提交的。但 如果这里的“所有副本”只包含一个同步副本,那么在这个副本变为不可用时,数据就会丢失。

如果要确保已提交的数据被写入不止一个副本,就需要把最少同步副本数量设置为大一点的值。对于一个包含 3 个副本的主题,如果 min.insync.replicas 被设为 2 ,那么至少要存在两个同步副本才能向分区写入数据。

如果 3 个副本都是同步的,或者其中一个副本变为不可用,都不会有什么问题。不过,如果有两个副本变为不可用,那么 broker 就会停止接受生产 者的请求。尝试发送数据的生产者会收到 NotEnoughReplicasException 异常。消费者仍然可以继续读取已有的数据。实际上,如果使用这样的配置,那么 当只剩下一个同步副本时,它就变成只读了,这是为了避免在发生不完全选举时数据的写入和读取出现非预期的行为。为了从只读状态中恢复,必须让 两个不可用分区中的一个重新变为可用的(比如重启 broker ),并等待它变为同步的。