Kafka系列——详解如何保证数据的可靠性

与性能一样,在系统的设计之初就应该 考虑可靠性问题,而不能在事后才来考虑。
同时,可靠性是系统的一个属性,而不是一 个独立的组件,所以在讨论 Kafka 的可靠性保证时,还是要从系统的整体出发。

Kafka 在数据传递可靠性方面具备很大的灵活性
我们知道,Kafka 可以被用在很多场景里,从跟踪用户点击动作到处理信用卡支付操作。
有些场景要求很高的可靠性,而有些则更看重速度和简便性。Kafka 被设计成高度可配置的,而且它的客户端 API 可以满足不同程度的可靠性需求
但有时候你的系统看起来是可靠的,但实际上有可能不是。本文先讨论各种各样的可靠性及其在 Kafka 场景中的含义。然后介绍 Kafka 的复制功能

可靠性保证

在讨论可靠性时,我们一般会使用保证这个词,它是指确保系统在各种不同的环境下能够发生一致的行为
所以,了解系统的保证机制对于构建可靠的应用程序来说至关重要,这也是能够在不同条件下解释系统行为的前提。在Kafka中可以提供以下保证:

  • 保证分区消息的顺序——分区有序。如果使用同一个生产者往同一个分区写入消息,而且消息 B 在消息 A 之后写入,那么 Kafka 可以保证消息 B 的偏移量比消息 A 的偏移量大, 而且消费者会先读取消息 A 再读取消息 B。
  • 只有当消息被写入分区的所有同步副本时(但不一定要写入磁盘),它才被认为是“已 提交”的

生产者可以选择接收不同类型的确认,比如在消息被完全提交时的确认,或者在消息被写入首领副本时的确认,或者在消息被发送到网络时的确认。

  • 只要还有一个副本是活跃的,那么已经提交的消息就不会丢失
  • 消费者只能读取已经提交的消息

这些基本的保证机制可以用来构建可靠的系统,但仅仅依赖它们是无法保证系统完全可靠的。
构建一个可靠的系统需要作出一些权衡,Kafka 管理员和开发者可以在配置参数上作 出权衡,从而得到他们想要达到的可靠性。这种权衡一般是指消息存储的可靠性和一致性 、的重要程度与可用性、高吞吐量、低延迟和硬件成本的重要程度之间的权衡。下
面将介绍 Kafka 的复制机制,并探讨 Kafka 是如何实现可靠性的,最后介绍一些重要的配置参数。

复制

Kafka 的复制机制和分区的多副本架构是 Kafka 可靠性保证的核心
把消息写入多个副本可以使 Kafka 在发生崩溃时仍能保证消息的持久性。

现在来回顾一下之前介绍的kafka复制功能:
Kafka 的主题被分为多个分区,分区是基本的数据块。分区存储在单个磁盘上,Kafka 可以保证分区里的事件是有序的,分区可以在线(可用),也可以离线(不可用)每个分区可以有多个副本,其中一个副本是首领。所有的事件都直接发送给首领副本,或者直接从首领副本读取事件
其他副本只需要与首领保持同步,并及时复制最新的事件。当首领副本不可用时,其中一个同步副本将成为新首领。

同步副本

分区首领是同步副本,而对于跟随者副本来说,它需要满足以下条件才能被认为是同步的

  • 与 Zookeeper 之间有一个活跃的会话,也就是说,它在过去的 6s(可配置)内向 Zookeeper 发送过心跳。
  • 在过去的 10s 内(可配置)从首领那里获取过消息。
  • 在过去的 10s 内从首领那里获取过最新的消息。光从首领那里获取消息是不够的,它还必须是几乎零延迟的

如果跟随者副本不能满足以上任何一点,比如与 Zookeeper 断开连接,或者不再获取新消 息,或者获取消息滞后了 10s 以上,那么它就被认为是不同步的
一个不同步的副本通过 与 Zookeeper 重新建立连接,并从首领那里获取最新消息,可以重新变成同步的。这个过程在网络出现临时问题并很快得到修复的情况下会很快完成,但如果 broker 发生崩溃就需要较长的时间。

非同步副本

如果一个或多个副本在同步和非同步状态之间快速切换,说明集群内部出现 了问题通常是 Java 不恰当的垃圾回收配置导致的
不恰当的垃圾回收配置会造成几秒钟的停顿,从而让 broker 与 Zookeeper 之间断开连接,最后变成不同步的,进而发生状态切换

一个滞后的同步副本会导致生产者和消费者变慢,因为在消息被认为已提交之前,客户端会等待所有同步副本接收消息。而如果一个副本不再同步了,我们就不再关心它是否已经收到消息。虽然非同步副本同样滞后,但它并不会对性能产生任何影响。但是,更少的同步副本意味着更低的有效复制系数,在发生宕机时丢失数据的风险更大

Broker配置

broker 有 3 个配置参数会影响 Kafka 消息存储的可靠性。与其他配置参数一样,它们可以应用在 broker 级别,用于控制所有主题的行为,也可以应用在主题级别,用于控制个别主题的行为。

在主题(Topic)级别控制可靠性,意味着 Kafka 集群可以同时拥有可靠的主题和非可靠的主题。
例如,在银行里,管理员可能把整个集群设置为可靠的,但把其中的一个主题设置为非可靠的,用于保存来自客户的投诉,因为这些消息是允许丢失的。
下面来逐个介绍这些配置参数,看看它们如何影响消息存储的可靠性,以及 Kafka 在哪 些方面作出了权衡。

复制系数

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

本系列会假设主题的复制系数都是3,也就是说每个分区总共会被 3 个不同的 broker 复制 3 次。这样的假设是合理的,因为 Kafka 的默认复制系数就是 3——不过用户可以修改它。即使是在主题创建之后,也可以通过新增或移除副本来改变复制系数。
如果复制系数为 N,那么在 N-1 个 broker 失效的情况下,仍然能够从主题读取数据或向主题写入数据。所以,更高的复制系数会带来更高的可用性、可靠性和更少的故障。另一方 面,复制系数 N 需要至少 N 个 broker,而且会有 N 个数据副本,也就是说它们会占用 N倍的磁盘空间。我们一般会在可用性和存储硬件之间作出权衡。

那么该如何确定一个主题需要几个副本呢?这要看主题的重要程度,以及愿意付出多少成本来换取可用性。有时候这与你的偏执程度也有点关系。
如果因 broker 重启导致的主题不可用是可接受的(这在集群里是很正常的行为),那么把复制系数设为 1 就可以了。在作出这个权衡的时候,要确保这样不会对你的组织和用户造 成影响,因为你在节省了硬件成本的同时也降低了可用性。
复制系数为 2 意味着可以容忍 1 个 broker 发生失效,看起来已经足够了。不过要记住,有时候 1 个 broker 发生失效会导 致集群不稳定(通常是旧版的 Kafka),迫使你重启另一个 broker——集群控制器。也就是 说,如果将复制系数设为 2,就有可能因为重启等问题导致集群不可用。所以这是一个两难的选择。

基于以上几点原因,我们建议在要求可用性的场景里把复制系数设为 3。在大多数情况下, 这已经足够安全了——不过我们也见过有些银行使用 5个副本,以防不测。

副本的分布也很重要默认情况下,Kafka 会确保分区的每个副本被放在不同的 broker 上
不过,有时候这样仍然不够安全。如果这些 broker 处于同一个机架上,一旦机架的交换机发生故障,分区就会不可用,这时候把复制系数设为多少都不管用。
为了避免机架级别的故障,我们建议把 broker 分布在多个不同的机架上,并使用 **broker.rack **参数来为每个 broker 配置所在机架的名字。如果配置了机架名字,Kafka 会保证分区的副本被分布在多 个机架上,从而获得更高的可用性。

不完全的首领选举

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.enable 设为 true,就是允许不同步的副本成为首领(也 就是“不完全的选举”),那么我们将面临丢失消息的风险。
如果把这个参数设为 false, 就要等待原先的首领重新上线,从而降低了可用性
我们经常看到一些对数据质量和数据 一致性要求较高的系统会禁用这种不完全的首领选举(把这个参数设为 false)。银行系统 是这方面最好的例子,大部分银行系统宁愿选择在几分钟甚至几个小时内不处理信用卡支 付事务,也不会冒险处理错误的消息。不过在对可用性要求较高的系统里,比如实时点击 流分析系统,一般会启用不完全的首领选举。

最少同步副本

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

我们知道,尽管为一个主题配置了 3 个副本,还是会出现只有一个同步副本的情况。如果这个同步副本变为不可用,我们必须在可用性和一致性之间作出选择——这是一个两难的选择。

根据 Kafka 对可靠性保证的定义,消息只有在被写入到所有同步副本之后才被认为是已提交的。但如果这里的“所有副本”只包含一个同步副本,那么在这个副本变为不可用时,数据就会丢失

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

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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值