Kafka作为分布式消息系统,在消息可靠性方面存在多个可能导致消息丢失的环节,主要有以下三个方面会存在丢失数据的可能性,分别是生产者、消费者以及kafka节点broker三个方面。
生产者端丢失消息原因及解决方案
生产者丢失消息原因
生产者端消息丢失主要发生在以下几个场景:
- 确认机制配置: ack=0(不等待确认)或者ack=1(仅Leader确认)
- 重试机制失败: 网络抖动时重试次数不足(默认retries=0)
- 异步发送未处理的异常: 生产者使用异步发送时,若消息发送失败(如网络波动,Broker节点宕机)且未设置回调处理异常,会导致消息丢失
- 消息体太大: 发送的消息体大小超过Broker设置的message.max.bytes的值,Broker节点会直接返回错误消息导致消息丢失
生产者丢失消息的解决方案
- 发送时,处理发送后的回调异常数据
使用同步发送(send().get())或异步发送时添加回调,捕获 Exception 并重试(如网络错误)
producer.send(record, (metadata, exception) -> {
if (exception != null) {
// 处理异常(如重试、记录日志)
exception.printStackTrace();
}
});
- 合理设置ack的参数
- 针对可靠性要求高的业务场景,设置ack = 1(或者ack=all),确保Leader和所有的follower都确认接收
- 配合min.insync.replicas(Broker参数),设置最小同步副本数,避免单节点故障导致数据丢失
- 调整缓冲区配置大小
- 增大buffer.memory,避免缓冲区满
- 合理设置retires参数(重试次数3)和设置retry.backoff.ms(重试时间间隔)失败时自动重试
- 确保max.block.ms(默认60s)时间足够长,避免缓冲区满时立即抛出异常
- 合理设置message.max.bytes的值,避免生产环境中存在消息体过大的情况
Broker端丢失消息原因及解决方案
Broker端丢失消息原因
- 脏选举情形:
unclean.leader.election.enable=true(非同步副本成为Leader)如果Leader宕机了,此时ISR机制里的follower节点还没有同步完消息就被选举为新的Leader,会导致数据丢失 - 刷盘策略设置不合理: kafka默认设置异步刷盘策略,依赖于操作系统的页缓存机制异步刷盘(log.flush.interval.messages:记录多少条消息即刷盘)和(log.flush.interval.ms:间隔多长时间即刷盘),此时如果Broker宕机了,页缓存中未刷盘的消息会丢失
- 副本数量设置不足: 如果设置了replication.factor=1(默认1),则Leader宕机后ISR没有副本可供选举,消息直接丢失
Broker端丢失消息的解决方案
- 限制Leader的选举范围: 设置unclean.leader.election.enable=false,仅允许ISR中的副本成本Leader,确保Leader包含最新的消息
- 合理设置副本数量及消息响应: 结合min.insync.replica 和ack=all,当同步的副本不足时,生产者端直接响应写入失败。
- 平衡刷盘的性能和可靠性: 合理设置(log.flush.interval.messages:记录多少条消息即刷盘),(log.flush.interval.ms:间隔多长时间即刷盘)和(mins.insync.replica:设置同步的副本数量),通过多副本保障 —— 即使Leader和 Broker 宕机,其他副本的页缓存 / 磁盘数据仍可用。
消费者端丢失消息原因及解决方案
消费端丢失消息原因
- 提前提交offset: 如果设置了自动提交(enable.auto.commit=true)时,若auto.commit.interval.ms设置过小,消费者可能在消息消费前自动提交offset,如果处理的时候崩溃,则未被处理的数据会丢失。
- 消费异常未处理: 消息处理失败(如业务逻辑抛异常),但未捕获异常,导致 Offset 仍被提交,消息被标记为已消费。
消费端丢失消息的解决方案
- 关闭自动提交: 关闭自动提交:
enable.auto.commit=false。在消息完全处理成功后手动提交(同步commitSync()确保提交成功,或异步commitAsync()配合回调)。 - 消费失败的消息特殊处理: 消费失败的消息可写入死信队列(DLQ),避免阻塞正常消费,同时便于后续排查和重试。
kafka消息丢失的其他场景
-
Topic 留存策略过严
- 原因:
retention.ms(消息留存时间,默认 7 天)或retention.bytes(留存大小)设置过小,消息被提前清理(未被消费即删除)。 - 解决:根据消费速度调整留存策略,确保消息在被消费前不被删除(如
retention.ms=604800000即 7 天)。
- 原因:
-
Broker 磁盘故障
- 原因:单 Broker 磁盘损坏,且消息未同步到其他副本。
- 解决:使用 RAID 磁盘阵列(如 RAID10)避免单点存储故障,结合
replication.factor≥2确保数据多副本存储。
-
网络分区(脑裂)
- 原因:集群网络分裂导致部分 Broker 失联,副本同步中断,可能引发数据不一致。
- 解决:合理配置
session.timeout.ms(如 10000ms)和heartbeat.interval.ms(如 3000ms),确保集群快速检测并恢复一致性。
总结
kafka可以通过一系列的机制来保证从生产者-》Broker端-》消费者这几个方面来保证消息不会丢失,首先是从生产者,设置确认响应消息机制(ack=0: 不推荐,ack=1: 写入Leader节点即返回,ack=all:写入Leader节点和所有的follower节点才返回,会降低吞吐量和性能),同时在生产端推送消息的异常需特殊处理,避免推送 过大的请求消息体将Broker端的消息缓冲区占满。
其次是Broker端,需要平衡性能和刷盘的机制,需合理设置(log.flush.interval.messages:记录多少条消息即刷盘),(log.flush.interval.ms:间隔多长时间即刷盘)和(mins.insync.replica:设置同步的副本数量),避免出现脏选举(Leader数据未同步Broker即宕机)和无副本选举的情况。
最后是消费端,需合理设置消息自动提交,如果消息仍然在消费,但是消息的offset已经提交到kafka,且消息被消费失败会导致消息永久丢失,建议手动关闭自动提交offset,同时消费端消费失败的消息需提交至死信队列,以便后续的排查分析。