undefined

一、kafka为什么吞吐量大?速度快

  1. 磁盘顺序读写
    使用磁盘的顺序读写,一般来说要高出磁盘随机读写三个数量级,一些情况下磁盘顺序读写性能甚至高于内存随机读写
    kafka的message是不断追加到本地磁盘文件末尾的
    每一个partition是一个文件,收到消息后kafka会把数据插入到文件末尾,但是没有办法删除数据,因此kakfka是不会删除数据的
    如何删除数据:1. 基于时间 2. 基于 partition 文件大小

  2. Page Cache
    为了优化读写性能,Kafka利用了操作系统本身的Page Cache,就是利用操作系统自身的内存而不是JVM空间内存。
    避免Object消耗:如果是使用 Java 堆,Java对象的内存消耗比较大,通常是所存储数据的两倍甚至更多。
    避免GC问题:随着JVM中数据不断增多,垃圾回收将会变得复杂与缓慢,使用系统缓存就不会存在GC问题
    相比于使用JVM或in-memory cache等数据结构,利用操作系统的Page Cache更加简单可靠。首先,操作系统层面的缓存利用率会更高,因为存储的都是紧凑的字节结构而不是独立的对象。其次,操作系统本身也对于Page Cache做了大量优化,提供了 write-behind、read-ahead以及flush等多种机制。再者,即使服务进程重启,系统缓存依然不会消失,避免了in-process cache重建缓存的过程。
    通过操作系统的Page Cache,Kafka的读写操作基本上是基于内存的,读写速度得到了极大的提升。

  3. 零拷贝
    sendfile
    当Kafka客户端从服务器读取数据时,如果不使用零拷贝技术,那么大致需要经历这样的一个过程:
    1.操作系统将数据从磁盘上读入到内核空间的读缓冲区中。
    2.应用程序(也就是Kafka)从内核空间的读缓冲区将数据拷贝到用户空间的缓冲区中。
    3.应用程序将数据从用户空间的缓冲区再写回到内核空间的socket缓冲区中。
    4.操作系统将socket缓冲区中的数据拷贝到NIC缓冲区中,然后通过网络发送给客户端。
    Kafka使用了零拷贝技术,也就是直接将数据从内核空间的读缓冲区直接拷贝到内核空间的socket缓冲区,然后再写入到NIC缓冲区,避免了在内核空间和用户空间之间穿梭。

  4. 分区分段+索引

    Kafka的message是按topic分类存储的,topic中的数据又是按照一个一个的partition即分区存储到不同broker节点。每个partition对应了操作系统上的一个文件夹,partition实际上又是按照segment分段存储的。这也非常符合分布式系统分区分桶的设计思想。

    通过这种分区分段的设计,Kafka的message消息实际上是分布式存储在一个一个小的segment中的,每次文件操作也是直接操作的segment。为了进一步的查询优化,Kafka又默认为分段后的数据文件建立了索引文件,就是文件系统上的.index文件。这种分区分段+索引的设计,不仅提升了数据读取的效率,同时也提高了数据操作的并行度。

  5. 批量读写

    Kafka数据读写也是批量的而不是单条的。

    除了利用底层的技术外,Kafka还在应用程序层面提供了一些手段来提升性能。最明显的就是使用批次。在向Kafka写入数据时,可以启用批次写入,这样可以避免在网络上频繁传输单个消息带来的延迟和带宽开销。假设网络带宽为10MB/S,一次性传输10MB的消息比传输1KB的消息10000万次显然要快得多。

  6. 批量压缩
    在很多情况下,系统的瓶颈不是CPU或磁盘,而是网络IO,对于需要在广域网上的数据中心之间发送消息的数据流水线尤其如此。进行数据压缩会消耗少量的CPU资源,不过对于kafka而言,网络IO更应该需要考虑。

    1. 如果每个消息都压缩,但是压缩率相对很低,所以Kafka使用了批量压缩,即将多个消息一起压缩而不是单个消息压缩

    2. Kafka允许使用递归的消息集合,批量的消息可以通过压缩的形式传输并且在日志中也可以保持压缩格式,直到被消费者解压缩

    3. Kafka支持多种压缩协议,包括Gzip和Snappy压缩协议

    Kafka速度的秘诀在于,它把所有的消息都变成一个批量的文件,并且进行合理的批量压缩,减少网络IO损耗,通过mmap提高I/O速度,写入数据的时候由于单个Partion是末尾添加所以速度最优;读取数据的时候配合sendfile直接暴力输出。

二、kafka 中 topic、broker、partition 的关系

一个topic对应多个partition,这些partition分布在多个broker上,多个broker一起提供kafka服务。

在kafka中,Topic是一个存储消息的逻辑概念,可认为是一个消息的集合。而物理上,不同Topic的消息分开存储,每个Topic可划分多个partition,同一个Topic下的不同的 partition 包含不同消息。每个消息被添加至分区时,分配唯一 offset,以此保证partition内消息的顺序性。

并且,kafka通过多副本复制技术,实现kafka集群的高可用和稳定性。每个partition都会有多个数据副本,每个副本分别存在于不同的broker。所有的数据副本中,有一个数据副本为Leader,其他的数据副本为follower。在kafka集群内部,所有的数据副本皆采用自动化的方式进行管理,并且确保所有的数据副本的数据皆保持同步状态。不论是producer端还是consumer端发往partition的请求,皆通过leader数据副本所在的broker进行处理。当broker发生故障时,对于leader数据副本在该broker的所有partition将会变得暂时不可用。Kafka将会自动在其他数据副本中选择出一个leader,用于接收客户端的请求。

在kafka的broker中,每个分区都会对照着文件系统的一个目录。在kafka的数据日志文件目录中,每个日志数据段都会分配两个文件,一个索引文件和一个数据文件。当前版本的kafka,每个broker会为每个日志段文件打开一个index文件句柄和一个数据文件句柄。因此,随着partition的增多,需要底层操作系统配置更高的文件句柄数量限制。

三、kafka 中如何保障消息的不丢失?处理重复消息?消息有序性?如何处理消息堆积?

1. 保障消息的不丢失?

【客户端实践和原理】消息丢失原理和无消息丢失配置

2. 处理重复消息?

【幂等生产者和事务型生产者】

3. 消息有序性

【保证消息有序性】

4. 消息堆积

消息的堆积往往因为生产者和生产速度与消费者的消费速度不匹配。有可能是因为消息消费失败反复重试导致,也有可能就是消费者消费能力弱,逐渐消息就积压

因此我们需要先定位消费慢的原因,如果是本身消费能力较弱,下游的数据处理不及时,我们可以优化下消费逻辑,提高每批次拉取的数量。

如果消费逻辑已经优化了,消费能力不足,则可以考虑增加 topic 的分区数,并同时提升消费组的消费者数量,消费者数 = 分区数