Kafka-可靠性保证-保证了哪些可靠性
ACID是关系型数据库普遍支持的标准可靠性保证。
ACID:原子性(atomicity)、一致性(consistency)、隔离性(isolation)、持久性(durability)
如果数据库遵循ACID规范,那么该数据库就支持与事务相关的行为。
kafka在哪些方面做出保证?
- 1.kafka可以保证分区消息的顺序。如果使用同一个生产者往同一个分区写入消息,而且消息B在消息A之后写入,那么kafka可以保证消息B的偏移量比消息A的偏移量大。而且消费者会先读取消息A再读取消息B。
- 2.只有当消息被写入分区的所有同步副本时(但不一定要写入磁盘),它才被认为是”已提交“的。生产者可以选择接收不同类型的确认,比如在消息被完全提交时的确认,或者在消息被写入首领副本时的确认,或者在消息被发送到网络时的确认。
- 3.只要还有一个副本是活跃的,那么已经提交的消息就不会丢失
- 4.消费者只能读取已经提交的消息。
Kafka-可靠性保证-复制机制
kafka的复制机制和分区的多副本架构是kafka可靠性保证的核心。把消息写入多个副本可以使kafka在发生崩溃时仍能保证消息的持久性。
kafka的主题被分为多个分区,分区是基本的数据块。分区存储在单个磁盘上,kafka可以保证分区里的事件是有序的,分区可以在线(可用),也可以离线(不可用)。每个分区可以有多个副本,其中一个副本是首领。所有的事件都直接发送给首领副本,或者直接从首领副本读取事件。其它副本只需要与首领保持同步,并及时复制最新的事件。当首领副本不可用时,其中一个同步副本将成为新首领。
分区首领时同步副本,而对于跟随者副本来说,他需要满足以下条件才能被认为是同步的。
- 1.与zookeeper之间有一个活跃的会话,跟随者副本在过去的6s(可配置)内向zookeeper发送过心跳
- 2.在过去的10s内(可配置)从首领那里获取过消息
- 3.再过去的10s内从首领那里获取过最新的消息。光从首领那里获取消息是不够的,它还必须是几乎零延迟的。
如果跟随者副本不能满足以上任何一点,比如与zookeeper断开连接,或者不再获取新消息,或者获取消息滞后10s以上,那么它就被认为是不同步的。一个不同步的副本通过与zookeeper重新建立连接,并从首领那里获取最新消息,可以重新变成同步的。这个过程在网络出现临时问题并很快得到修复的情况下会很快完成,但如果broker发生崩溃就需要较长时间。
一个滞后的同步副本会导致生产者和消费者变慢,因为在消息被认为已提交之前,客户端会等待所有同步副本接收消息。而如果一个副本不再同步了,我们就不再关心它是否已经收到消息。虽然非同步副本同样滞后,但它并不会对性能产生任何影响。但是,更少的同步副本意味着更低的有效复制系数,在发生宕机时丢失数据的风险更大。
注意:如果一个或多个副本在同步和非同步状态之间快速切换,说明集群内部出现了问题,通常是Java不恰当的垃圾回收配置导致的。不恰当的垃圾回收配置会造成几秒钟的停顿,从而让broker与zookeeper之间断开连接,最后变成不同步的,进而发生状态切换。
Kafka-可靠性保证-broker配置
broker有3个配置参数会影响Kafka消息存储的可靠性。与其它配置参数一样,它们可以是broker级别的,用于控制所有主题的行为,也可以应用在主题级别,用于控制个别主题的行为。
在主题级别控制可靠性,意味着kafka集群可以同时拥有可靠的主题和非可靠的主题。
1.复制系数
主题级别的配置参数是replication.factor,而在broker级别则可以通过default.replication.factor来配置自动创建的主题。
假设主题的复制系数都是3,也就是说每个分区总共会被3个不同的broker复制3次。kafka的默认复制系数是3,不过用户可以对它进行修改。即使是在主题创建之后,也可以通过新增或移除副本来改变复制系数。
如果复制系数为N,那么在N-1个broker失效的情况下,仍然能够从主题读取数据或向主题写入数据。所以,更高的复制系数会带来更高的可用性、可靠性和更少的故障。
复制系数为N就需要至少N个broker,而且会有N个数据副本,也就是说他们会占用N倍的磁盘空间。需要在可用性和存储硬件之间做出权衡。
如何确定一个主体需要几个副本?要看主题的重要程度,以及愿意付出多少成本换去可用性。
如果因broker重启导致的主体不可用是可接受的,那么把复制系数设为1就可以了。在作出这个权衡的时候,要确保这样不会对消息使用方造成影响,因为在节省了硬件成本的同时降低了可用性。
建议把复制系数设为3,大多数情况下,这已经足够安全,也有些银行会使用5个副本,以防不测。
副本的分布也很重要。默认情况下,kafka会确保分区的每个副本被放在不同的broker上。不过,有时候这样仍然不够安全,因为如果这些broker处于同一个机架上,一旦机架的交换机发生故障,分区就会不可用,这时候把复制系数设为多少都不管用。为了避免机架级别的故障,我们建议把broker分布在多个不同的机架上,并使用broker.rack参数来为每个broker配置所在机架的名字。如果配置了机架名字,kafka会保证分区的副本被分布在多个机架上,从而获得更高的可用性。
2.不完全的首领选举
unclean.leader.election.enable只能在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),那么就要承担丢失数据和出现数据不一致的风险。
如果我们不允许不同步的副本成为首领(unclean.leader.election.enable设为false),那么就要接受较低的可用性,因为我们必须等待原先的首领恢复到可用状态。
一些对数据质量和数据一致性要求较高的系统会禁用这种不完全的首领选举,宁可花点时间等待旧分区首领的恢复,也不接受数据的丢失和数据的不一致性。
一些对可用性要求较高的系统里,比如实时点击流分析系统,一般会启用不完全的首领选举。
3.最少不同步副本
设置最少不同步副本,使用min.insync.replicas,在主题级别和broker级别都可以设置。
如果要确保已提交的数据被写入不止一个副本,就需要把最少同步副本数量设置为大一点的值。对于一个包含3个副本的主题,如果min.insync.replicas被设为2,那么至少要存在两个同步副本才能向分区写入数据。
如果3个副本都是同步的,或者其中一个副本变为不可用,都不会有什么问题。不过,如果有两个副本变为不可用,那么broker就会停止接受生产者的请求。尝试发送数据的生产者会收到NotEnoughReplicasException异常。消费者仍然可以继续读取已有的数据。实际上,如果使用这样的配置,那么当只剩下一个同步副本时,它就变成只读了,这是为了避免在发生不完全选举时数据的写入和读取出现非预期的行为。为了从只读状态中恢复,必须让两个不可用分区中的一个重新变为可用的(比如重启broker),并等待它变为同步的。