Kafka学习整理

1 名词解释

image.png
  • Topic:可以认为一个一类消息,生产者和消费者通过topic进行数据传输
  • partition:Kafka 中消息是以 topic 进行分类的,生产者通过 topic 向 Kafka broker 发送消息,消费者通过 topic 读取数据。如果 partition 规则设置的合理,所有消息可以均匀分布到不同的 partition 里,这样就实现了水平扩展(解决了性能问题)。同时生产者可以通过设置key指定分片,实现局部有序

2 存储原理

随着生产者不断生产消息,partition在实际保存的时候会不断扩张,对于消息文件的维护和过期数据的清理带来影响,所以partition又细分成segment,方便过期数据的清理。

segment 文件由两部分组成,分别为“.index”文件(索引文件,存储大量的元数据)和“.log”文件(存储大量的消息),分别表示为 segment 索引文件和数据文件。这两个文件的命令规则为:partition 全局的第一个 segment 从 0 开始,后续每个 segment 文件名为上一个 segment 文件最后一条消息的 offset 值,数值大小为 64 位,20 位数字字符长度,没有数字用 0 填充,如下:

00000000000000000000.index
00000000000000000000.log
00000000000000170410.index
00000000000000170410.log
00000000000000239430.index
00000000000000239430.log

以上面的 segment 文件为例,展示出 segment:00000000000000170410 的“.index”文件和“.log”文件的对应的关系,如下图:


image.png

如上图,索引文件中的元数据指向对应数据文件中 message 的物理偏移地址。其中以“.index”索引文件中的元数据 [3, 348] 为例,在“.log”数据文件表示第 3 个消息,即在全局 partition 中表示 170410+3=170413 个消息,该消息的物理偏移地址为 348。

那么如何从 partition 中通过 offset 查找 message 呢?以上图为例,读取 offset=170418 的消息,首先查找 segment 文件,其中 00000000000000000000.index 为最开始的文件,第二个文件为 00000000000000170410.index(起始偏移为 170410+1=170411),而第三个文件为 00000000000000239430.index(起始偏移为 239430+1=239431),所以这个 offset=170418 就落到了第二个文件之中。其他后续文件可以依次类推,以其实偏移量命名并排列这些文件,然后根据二分查找法就可以快速定位到具体文件位置。其次根据 00000000000000170410.index 文件中的 [8,1325] 定位到 00000000000000170410.log 文件中的 1325 的位置进行读取。

3 ISR

ISR (In-Sync Replicas),指副本同步队列,由 leader 维护 ,通过ISR维护正常同步的副本集合(包括leader和follower),在ISR列表中的副本的延迟条数replica.lag.time.max.ms在设置的范围内,超过阈值会把 follower 剔除出 ISR。

Kafka 的 ISR 的管理最终都会反馈到 Zookeeper 节点上,通过leader单独的线程定期检测 follower,并将新的 ISR 的信息返回到 Zookeeper 的相关节点中。

为什么需要ISR?
Leader宕机后通常需要重新选举Leader,同时为了保证数据不丢失,新Leader必须包含完整的数据。同步复制要求所有能工作的follower副本都复制完,这条消息才会被确认已成功提交,这种复制方式极大的影响了性能。kafka使用这种ISR的方式,只维护了部分符合要求的副本,有效的权衡了数据可靠性和性能之间的关系。

为什么不使用类似ZK的“少数服从多数”原则?
如果可以容忍N个节点挂掉,那么就需要部署2N+1个节点;。也就是说,在生产环境下为了保证较高的容错率,必须要有大量的副本,而大量的副本又会在大数据量下的协商会导致性能的急剧下降(也是kafka为什么不在zk里保存offset的原因)。

4 HW 和 LEO

LEO(Log End Offset),标识当前日志文件中下一条待写入的消息的offset;
HW (High Watermark),取一个 partition 对应的 ISR 中最小的 LEO 作为 HW,consumer 最多只能消费到 HW 所在的位置。
假如leader副本的LEO为5,follower1的LEO为5,follower2的LEO 为4,那么当前分区的HW取最小值4,此时消费者可以消费到offset0至3之间的消息;当所有副本都成功写入消息3和消息4之后,整个分区的HW和LEO都变为5,因此消费者可以消费到offset为4的消息了。

为什么需要HW和LEO?
保证副本数据一致性,因为HW代表了ISR所有副本中都已经同步的数据,而log是直接做追加操作,要先截断到HW位置,保证自己没有多余的数据,然后在追加

5 数据可靠性保证

当 producer 向 leader 发送数据时,可以通过 request.required.acks 参数来设置数据可靠性的级别:

  • 0: 意味着 producer 无需等待来自 broker 的确认而继续发送下一批消息。这种情况下数据传输效率最高,但是数据可靠性确是最低的。
  • 1:这意味着 producer 在 ISR 中的 leader 已成功收到的数据并得到确认后发送下一条 message。如果 leader 宕机了,则会丢失数据。
  • -1: producer 需要等待 ISR 中的所有 follower 都确认接收到数据后才算一次发送完成,可靠性最高。(生产环境下如果不能容忍数据丢失,应该设置为-1,并且配合min.insync.replicas>=2来保证完全不丢失,如果min.insync.replicas设置为1,当 ISR 中只有 leader 时,这样就变成了 acks=1 的情况。)

kafka会不会因为操作系统pagecache导致数据丢失
为了减少落盘次数,提高效率,kafka的broker会先存储到页缓存(Page cache)中,按照时间或者其他条件进行刷盘(从page cache到file);如果生产速度和消费速度速度相差不大,是可以依赖page cache直接完成数据交互的

但是当leader写入pagecache后宕机,数据并没有来得及落盘,数据会丢失;如果想保证数据不丢失,可以依赖request.required.acks = -1来保证副本都已经同步数据了,相当于kafka并不需要担心pagecache引起的数据丢失,而是使用follower副本避免数据丢失问题

6 消费者offset移交

consumer提交offset有两种方式:

  • 自动提交,让消费者自动提交偏移量。 enable.auto.comnit 被设为 true,消费者会自动把从 poll()方法接收到的最大偏移量提交上去。 提交时间间隔由 auto.commit.interval.ms 控制,默认值是 5s。因为太省事了,以至于丧失了很大的灵活性和可控性,完全没法把控 Consumer 端的位移管理。
  • 手动提交,包含同步提交 、异步提交 、异步+同步 组合的方式提交三种方式,在保证消费端消费完成后手动调用提交,可以保证消息不丢失(前提是客户端没有新开线程去处理)

offset提交到哪里?
这个提交过程不仅要实现高持久性,还要支持高频的写操作,显然,Kafka 的主题设计天然就满足这两个条件,因此kafka通过内置的_consumer_offset,用于Offset数据的提交(由于ZK并不适合大数据量写,所以改为topic保存)。

如果位移主题是 Kafka 自动创建的,那么该主题的分区数是 50,副本数是 3。并通过Math.abs(“groupid”.hashCode())%groupMetadataTopicPartitionCount 定位到分区位置。

随着消费者的不断消费,会向位移主题中不断写入消息,但是显然 Kafka 只需要保留这类消息中的最新一条就可以了,之前的消息都是可以删除的。这就要求 Kafka 必须要有针对位移主题消息特点的消息删除策略,否则这种消息会越来越多,最终撑爆整个磁盘。

Kafka 使用Compact 策略来删除位移主题中的过期消息,避免该主题无限期膨胀。那么应该如何定义 Compact 策略中的过期呢?对于同一个 Key 的两条消息 M1 和 M2,如果 M1 的发送时间早于 M2,那么 M1 就是过期消息。Kafka 提供了专门的后台线程定期地巡检待 Compact 的主题,看看是否存在满足条件的可删除数据。

7 kafka延时队列

https://www.jianshu.com/p/0ca873f7ea33

你可能感兴趣的:(Kafka学习整理)