一、会丢数据的情况
1、生产端
可通过 producer.type 来选择发送模式,默认为 producer.type = sync (同步),异步设置为 async
1)同步模式下
Producer 在发送消息之后,在得到返回结果前阻塞。这是一种牺牲性能的办法,而且对于不同的配置,性能的损失程度不同、可靠性也不一样。关键参数如下
request.required.acks 表示producer的一次请求被认为是完成时,需要的 ack 确认数,这里的 ack 具体是指哪些 broker 已经提交消息到log日志文件中。
为 0 时表示不需要等待来自broker的确认信息,可实现最大的吞吐量,但是这种发出去就不管了的方式可能会丢失数据。因为 message 放入 socket buffer 即认为已经发送,没有任何机制来保证broker成功接收数据。重试机制也不会起作用。
为 1 时表示等 leader 所在 broker 已经成功将消息写入到本地log中返回确认即可,但是如果 leader 返回确认后,fllower 还没有来的及从leader同步message而 leader 就挂掉了,此时也会丢数据。
为 -1 时 producer 会获得leader及所有同步replicas都收到数据的确认,同步replicas数由 min.insync.replicas 确定。
2)异步模式下
异步发送模式下,可以批量处理请求,消息先被填入缓冲区当中,当达到 batch.size 时或者达到 linger.ms 配置的等待时间发送缓冲区数据到broker。
这种情况下,如果客户端挂了,那么便会丢失未发送数据。
2、消费端
enable.auto.commit=true
一般情况下,自动提交offset默认为true,也就是在consumer拉取一批数据时会自动提交最新的消费偏移给kafka,表示我已经消费到哪里了。
这样的话,如果consumer拉取并提交了最新的 offset ,而 consumer 当前的message还没有处理完,这时consumer挂了,恢复时从最新提交的 offset 处理,这么一来便丢失了数据。
解决这个问题,可以在消息处理完成后手动提交 offset。
3、broker
unclean.leader.election.enable = true 从Kafka 0.11.0.0 之后 默认值改为了 false
这个参数确定了什么事呢?也就是确定不在同步副本当中的broker是否可以参与新leader的选举
1)参与
如果在ISR中的同步副本同时下线,这时如果unclean.leader.election.enable = true,那么不在ISR中的副本就可以参与选举,此时机器还是可用的。
但是一个问题是,不在ISR中的副本,其偏移会落后于ISR中的副本,当其成为leader之后,接收来自客户端的消息。之前下线的leader恢复之后成为follower,去新的leader同步消息,发现自己的Log End Offset大于新leader的Log End Offset,必须进行截断(follower不能比leader的LEO大),此时便造成了丢数据和不一致的情况。
2)不参与
如果unclean.leader.election.enable = false,那么不在ISR中的副本就不可以参与选举,此时如果在ISR中的同步副本同时下线,kafka服务便不可用了。
4、刷盘方式
1)同步刷盘
写入页缓存,线程等待,通知刷盘线程刷盘,刷盘线程刷盘后,唤醒前端等待线程,等待线程向用户返回写入结果,也就是进行ack
可见,同步等待落盘后确认,不会出现掉电数据未刷盘的情况,但对性能的影响是显而易见的
2)异步刷盘
写入页缓存,线程返回,向用户返回写入结果,刷盘由操作系统完成
如果线程返回ack,而broker所在节点突然掉电导致页缓存未刷盘,就丢失了数据。
和主动刷盘相关的几个配置
log.flush.interval.messages 设置刷盘前在内存中最多累积的消息数
log.flush.interval.ms 设置消息在刷盘之前最多在内存中保存的ms数
log.flush.scheduler.interval.ms 设置log flusher线程检查是否有消息需要刷盘的检查频度
在kafka中,数据可靠性主要靠分布式副本冗余来完成,避免单点故障,所以不出现极端的情况,异步刷盘也没有问题。
总之,万事无绝对,性能和可靠性之间总是要进行权衡,根据需要来进行选择吧。