深入理解kafka_深入理解Kafka数据高并发写入、可靠性以及EOS语义

来源公众号:架构师社区

Kafka作为一个优秀的高性能消息中间件,广泛用于各种大数据高并发场景下,常常听一些技术大牛讲起kafka滔滔不绝,赞不绝口,但是它是如何保证数据的高并发写入,可靠性以及流数据处理中常见的EOS语义的呢?本篇文章让我们来一起深入探究其内部原理。

一、 高并发写入

作为一个消息队列,我们首先需要考虑消息如何传递,如何存储。在高并发场景下,我们常常会想到如何提高系统的吞吐量,Kafka在生产者写入消息的时候会将数据最终写入磁盘,既然它是基于磁盘读写,那么频繁的IO操作肯定会影响读写的性能,为何会有高性能呢?

1. 系统缓存+顺序写

在这里,Kafka生产者将消息写入各个broker中的时候,并不会直接写入磁盘,在这中间有一些缓存,被称为Page Cache,它是基于操作系统的缓存,因为也被称为OS Cache,数据写入的时候,会将数据写入缓存,然后由操作系统决定什么时候写入磁盘,可以看到,写入数据时是直接和内存交互,因此其写入性能很高,当然不仅仅如此,在从缓存写入磁盘的时候,它会将随机写优化为顺序写,我们都知道,磁盘的写入是基于磁道寻址的,随机写会引发大量的磁盘寻址,浪费大量的时间,而顺序写避免了频繁的寻址操作,写入性能提高了数倍。(画外音:很多优秀的开源框架都采用了顺序来优化写入,比如HBase memestore)

236951ba68affe204b0949371fc85637.png

以上是我们从生产者的角度来分析其高并发写入,下面我们从消费者的角度来分析。

2.零拷贝技术

上面提到数据会写入磁盘,那么读取数据时需要直接和磁盘交互,也会影响其读取的性能,传统的从磁盘读取数据需要进行系统间内核的切换,操作系统需要读写磁盘数据到本地缓存中,然后系统切换到用户态将数据拷贝到应用缓存中,此时应用程序才会读取缓存中的数据进行操作。可以看出这种方式严重影响了读取的性能。

07890085cc5ce874292435253b60ae15.png

那么Kafka是怎么读取数据的呢?这里采用了零拷贝技术(画外音:JDK文件的拷贝也应用到了这种技术,比如transferTo API,可以参考我的笔记 文件拷贝的几种方式以及对比)。所谓零拷贝技术就是避免操作系统内核态到用户态的切换,直接基于内核,避免了不必要的上下文切换与拷贝,大大的提高了性能。

ff58ffb7a59014a9457f5189fb40691b.png

二、可靠性

同样,我们从生产者和消费者的角度来分析其可靠性。

试想,生产者在向broker中发送数据的时候,如何知道Broker是否收到消息了呢?万一网络断开怎么办?(画外音:想想TCP是如何实现可靠传输的?)。

在这里,我们需要知道kafka在向broker写入数据的时候是分为partition进行写入的,并且在不同的broker保存了相同的副本,并且这些副本之间会有一个主副本。具体在broker中是如何分配的呢?本篇文章不进行讨论,后续文章中会进行介绍。

2ec06c627f45d184ecc08bec1b8ebfbc.png

在kafka中,有一个acks参数可以来设置,这个参数表示必须有多少个分区副本收到消息,Kafka才会认为写入成功。可以有三个选项来进行设置:

acks=0,这种方式表示生产者不会等待服务器是否收到消息的答复,也就是说即使由于网络问题服务器没有收到消息,生产者也不会知道,这种方式适合用于数据要求不高的场合,其性能很高。

acks=1,这种方式表示生产者需要等待接收主节点的应答消息才会知道数据写入成功,如果没有收到应答信息或者当前集群因为选举没有主节点,此时会抛出异常。但是如果一台节点没有收到消息成为了主节点,此时会数据还是会丢失。

acks=all,这种方式表示生产者需要收到所有服务器的应答信息,才会知道消息发送成功,这种方式最为安全,但是其吞吐量低。

那么,kafka又是如何保证消息的顺序性呢?(画外音:想想TCP如何实现数据的有序性的?)

kafka可以保证分区消息的有序性,即如果有一个分区被多个生产者写入,如果A比B先写入,那么会保证B的偏移量大于A的偏移量,而且保证A比B先消费。

ISR机制

大部分环境下,我们设置acks=1,如果设置为all那么其性能严重下降,在大数据场景下根本不适用。那么,我们来考虑一个问题,如果生产者在向leader节点写入数据,然后同步给每个follower,但是如果此时follower宕机了怎么办?有人说搭建高可用集群,那我们再来分析一下,如果leader节点还没来得及同步给其他节点就宕机了,那么follower选举出一个新的leader对外提供服务,但是上一条数据已经丢失了,还是没有达到数据的可靠性。

在这里,我们就介绍到了kafka的ISR机制,什么是ISR机制呢?

简单地说就是在每个partition中会维护着一个ISR列表,这个列表中一定会包含leader,还有与它同步的follower,只要某个follower会同步leader的数据,那么肯定会在该列表中。如果某个follower因为自身发生问题,不能同步数据,那么会被认为“out of sync”,从ISR列表中删除。

Kafka的ISR机制保证了ISR列表中至少leader写入数据成功并且至少有一个follower同步完成leader的数据,才会认为消息发送成功,否则会认为发送失败,一直重试。

7915b1e4a42126f764abce813c603374.png

三、EOS语义

所谓EOS语义指 Exactly Once语义,表示数据有且仅被处理一次,在常见的流数据处理场景中,都存在着这种问题(画外音:在spark的低版本中并不能保证这种语义,Flink中可以保证该语义)。

在kafka的老版本中,并不能保证EOS语义。

例如。broker可能在提交消息和返回ack给生产者中间宕机,在这种情况下,生产者会由于没有收到响应而重试,从而导致消息流的重复。因此,生产者请求的幂等性是非常重要的,这能够保证即便出现重试或者broker故障,每条消息也只会出现一次。

ebdd1ddc37a267026e8dc1c5a1c705a1.png

不过让人庆幸的是,在2018年发布的kafka 0.11版本中,保证了该语义。

在生产者初始化消息的时候,会生成一个唯一ID。PID和一个序列号会包含在消息中,一起被发送到broker。序列号从0开始单调递增,对于每一个PID/TopicPartition对来说,当且仅当消息的序列号比上一次提交消息的序列号刚好大1,broker才会接收这个消息。如果不是消息重复的话,生产者会重发消息。

a18088d661c721af0eaeeb4981dae182.png

以上就是今天的全部内容,我们深入分析了Kafka是如何保证高并发写入,可靠性以及EOS语义。

end:如果你觉得本文对你有帮助的话,记得关注点赞转发,你的支持就是我更新动力。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值