✅为什么Kafka没办法100%保证消息不丢失?

✅为什么Kafka没办法100%保证消息不丢失?

典型回答

Kafka提供的Producer和Consumer之间的消息传递保证语义有三种,所谓消息传递语义,其实就是Kafka的消息交付可靠保障,主要有以下三种:

  • At most once—消息可能会丢,但绝不会重复传递;
  • At least once—消息绝不会丢,但可能会重复传递;
  • Exactly once—每条消息只会被精确地传递一次:既不会多,也不会少;

目前,Kafka 默认提供的交付可靠性保障是第二种,即At least once ,但是,其实依靠Kafka自身,是没有办法100%保证可靠性的。

✅Kafka如何保证消息不丢失?

上面的文档中,介绍了Kafka在保证消息的可靠性中做的一些努力,但是我们提到,Kafka只对已提交的消息做最大限度的持久化保证不丢失,但是没办法保证100%。


那么,整体分析下为什么吧。


生产者

Kafka允许生产者以异步方式发送消息,这意味着生产者在发送消息后不会等待确认。当然,我们可以注册一个回调等待消息的成功回调。

但是,如果生产者在发送消息之后,Kafka的集群发生故障或崩溃,而消息尚未被完全写入Kafka的日志中,那么这些消息可能会丢失。虽然后续有可能会重试,但是,如果重试也失败了呢?如果这个过程中刚好生产者也崩溃了呢?那就可能会导致没有人知道这个消息失败了,就导致不会重试了。

消费者

消费者来说比较简单,只要保证在消息成功时,才提交偏移量就行了,这样就不会导致消息丢失了。

Broker

Kafka使用日志来做消息的持久化的,日志文件是存储在磁盘之上的,但是如果Broker在消息尚未完全写入日志之前崩溃,那么这些消息可能会丢失了。

而且,操作系统在写磁盘之前,会先把数据写入Page Cache中,然后再由操作系统中自己决定什么时候同步到磁盘当中,而在这个过程中,如果还没来得及同步到磁盘中,就直接宕机了,那这个消息也就丢了。

当然,也可以通过配置<font style="color:rgb(18, 18, 18);">log.flush.interval.messages=1</font>,来实现类似于同步刷盘的功能,但是又回到了前面说的情况,还没来得及做持久化,就宕机了。

即使Kafka中引入了副本机制来提升消息的可靠性,但是如果发生同步延迟,还没来及的同步,主副本就挂掉了,那么消息就可能会发生丢失。

这几种情况,只从Broker的角度分析,Broker自身是没办法保证消息不丢失的,但是如果配合Producer,再配合request.required.acks = -1 这种ACK策略,可以确保消息持久化成功之后,才会ACK给Producer,那么, 如果我们的Producer在一定时间段内,没有收到ACK,是可以重新发送的。

但是,这种重新发送,就又回到了我们前面介绍生产者的时候的问题,生产者也有可能挂,重新发送也有可能会没有发送依据,导致消息最终丢失。

所以,我们说,只靠Kafka自己,其实是没有办法保证极端情况下的消息100%不丢失的。


但是,我们也可以在做一些机制来保证,比如引入分布式事务,或者引入本地消息表等,保证在Kafka Broker没有保存消息成功时,可以重新投递消息。这样才行。


✅常见的分布式事务有哪些?