【kafka】kafka消费策略 & kafka存储机制 & segment file & 稀疏存储
前言
这篇写了kafka的几个比较复杂的特性
kafka消费策略
结论:
-
在同一个消费者组中,不同的线程在同一时间不能够处理同一分区中的数据,也就是说在同一时间一个线程只能消费一个分区数据
-
在不同的消费者里面,不同的线程在同一时间可以消费同一分区中的数据
-
多个线程在消费数据的时候,只能够保证
每一个分区内部有序(按照数据进入的先后),而全局是没有顺序的
。
可以极端一点,只设置一个分区,那么就保证全局有序,这个时候只有一个分区,跟对应kafka分布式框架理念相违背
kafka存储机制
Partition是以文件的形式存储在文件系统中,
比如,创建了一个名为 kafkatest 的topic,其有3个partition,那么在Kafka的数据目录中(由配置文件中的log.dirs指定的)中就会有这样3个目录: kafkatest-0,kafkatest-1,kafkatest-2,其命名规则为<topic_name>-<partition_id>
,里面存储的分别就是这3个partition的数据。
每一个partition目录下的文件被平均切割成大小相等(默认一个文件是1G,可以手动去设置)
的数据文件,每一个数据文件都被称为一个段(segment file)
,但每个段消息数量不一定相等,这种特性能够使得老的segment可以被快速清除。默认保留7天的数据。
每次满1G后,在写入到一个新的文件中。
数据消费问题讨论
1、如何保证消息消费的有序性呢?
比如说生产者生产了0到100个商品,那么消费者在消费的时候按照0到100这个从小到大的顺序消费
2、那么kafka如何保证这种有序性呢?
难度就在于,生产者生产出0到100这100条数据之后,通过一定的分组策略存储到broker的partition中的时候,
比如0到10这10条消息被存到了这个partition中,10到20这10条消息被存到了那个partition中,这样的话,消息在分组存到partition中的时候就已经被分组策略搞得无序了。
3、那么能否做到消费者在消费消息的时候全局有序呢?
遇到这个问题,我们可以回答,在大多数情况下是做不到全局有序的。但在某些情况下是可以做到的。
------->比如partition只有一个,这种情况下是可以全局有序的。
4、那么可能有人又要问了,只有一个partition的话,哪里来的分布式呢?哪里来的负载均衡呢?
所以说,全局有序是一个伪命题!全局有序根本没有办法在kafka要实现的大数据的场景来做到。但是我们只能保证当前这个partition内部消息消费的有序性。
结论:一个partition中的数据是有序的吗?回答:间隔有序,不连续。
针对一个topic里面的数据,只能做到partition内部有序,不能做到全局有序
。特别是加入消费者的场景后,如何保证消费者的消费的消息的全局有序性,
这是一个伪命题,只有在一种情况下才能保证消费的消息的全局有序性,那就是只有一个partition。
Segment文件特点
segment文件命名的规则:partition全局的第一个segment从0(20个0)开始,后续的每一个segment文件名是上一个segment文件中最后一条消息的offset值
那么这样命令有什么好处呢?
假如我们有一个消费者已经消费到了368776(offset值为368776),那么现在我们要继续消费的话,怎么做呢?
分2个步骤:
- 第1步是从所有文件log文件的的文件名中
找到对应的log文件
,第368776条数据位于下图中的“00000000000000368769.log”这个文件中,这一步涉及到一个常用的算法叫做“二分查找法
”(假如我现在给你一个offset值让你去找,你首先是将所有的log的文件名进行排序,然后通过二分查找法进行查找,很快就能定位到某一个文件,紧接着拿着这个offset值到其索引文件中找这条数据究竟存在哪里); - 第2步是到index文件中去找第368776条数据所在的位置。
- 索引文件(index文件)中存储这大量的元数据,而数据文件(log文件)中存储着大量的消息。
- 索引文件(index文件)中的元数据指向对应的数据文件(log文件)中消息的物理偏移地址。
kafka的稀疏存储
上图的左半部分是索引文件,里面存储的是一对一对的key-value,其中key是消息在数据文件(对应的log文件)中的编号,比如“1,3,6,8……”,分别表示在log文件中的第1条消息、第3条消息、第6条消息、第8条消息……
那么为什么在index文件中这些编号不是连续的呢?
这是因为index文件中并没有为数据文件中的每条消息都建立索引,而是采用了稀疏存储的方式,每隔一定字节的数据建立一条索引
。
这样避免了索引文件占用过多的空间,从而可以将索引文件保留在内存中。但缺点是没有建立索引的Message也不能一次定位到其在数据文件的位置,从而需要做一次顺序扫描,但是这次顺序扫描的范围就很小了。
其中以索引文件中元数据3,497为例,其中3代表在右边log数据文件中从上到下第3个消息(在全局partiton表示第368772个消息),其中497表示该消息的物理偏移地址(位置)为497。
总结
- Kafka把topic中一个parition大文件分成多个小文件段,通过多个小文件段,就容易定期清除或删除已经消费完文件,减少磁盘占用。
- 通过索引信息可以快速定位message。
- 通过index元数据全部映射到memory,可以避免segment file的IO磁盘操作。
- 通过索引文件稀疏存储,可以大幅降低index文件元数据占用空间大小。