概念
topic物理上的分组,一个topic可以分为多个partition,每个partition是一个有序的队;在磁盘上以文件夹的形式存在;消息最终以文件形式保存在partition文件夹下面,分段存储。
- segment文件:
对于一个partition(在Broker中以文件夹的形式存在),里面又有很多大小相等的segment数据文件(这个文件的具体大小可以在config/server.properties中log.segment.bytes=XXX属性设置),这种特性可以方便old segment file的快速删除。
下面先介绍一下partition中的segment file的组成:
- segment file 组成:由2部分组成,分别为index file和data file,这两个文件是一一对应的,后缀”.index”和”.log”分别表示索引文件和数据文件;
- segment file 命名规则:partition的第一个segment从0开始,后续每个segment文件名为上一个segment文件最后一条消息的offset,ofsset的数值最大为64位(long类型),20位数字字符长度,没有数字用0填充。如下图所示
关于segment file中index与data file对应关系图,这里我们选用网上的一个图片,如下所示:
segement索引文件中存着大量的元数据、数据文件中存储着大量消息;索引文件中的元数据指向数据文件中的消息(message)物理偏移地址。以3,497为例:3表示数据文件的第3个消息(在全局的partition中是第368772个)497表示该消息的物理偏移地址为497
注:Partition中的每条message由offset来表示它在这个partition中的偏移量,这个offset并不是该Message在partition中实际存储位置,而是逻辑上的一个值(如上面的3),但它却唯一确定了partition中的一条Message(可以认为offset是partition中Message的id)。
为什么消息要加分段、索引
我们首先试想一下,如果对于Kafka的一个topic而言,如果topic的partition中只有一个数据文件的话会怎么样?
- 新数据是添加在文件末尾的,不论文件有多大,时间复杂度永远是O(1)
- 查找某个offset的Message永远是顺序查找,如果数据量很大,查找效率就很低,和ArrayList一样
如何解决这个问题?Kafka有2大法宝:分段和索引
- 数据文件的分段: 这个是比较好理解的,加入有100条message,它们的offset是从0到99,假设将数据文件分为5端,第一段为0-19,第二段为20-39,依次类推,每段放在一个单独的数据文件里面,数据文件以该段中最小的offset命名。这样在查找指定offset的Message的时候,用二分查找就可以定位到该Message在哪个段中。
- 为数据文件建索引:数据文件分段使得可以在一个较小的数据文件中查找对应offset的message了,但是这依然需要顺序扫描才能找到对应offset的message。为了进一步提高查找的效率,Kafka为每个分段后的数据文件建立了索引文件,文件名与数据文件的名字是一样的,只是文件扩展名为.index。
索引文件中包含若干个索引条目,每个条目表示数据文件中一条message的索引。索引包含两个部分(均为4个字节的数字),分别为相对offset和position
- 相对offset :因为数据文件分段以后,每个数据文件的起始offset不为0,相对offset表示这条message相对于其所属数据文件中最小的offset的大小。举例,分段后的一个数据文件的offset是从20开始,那么offset为25的message在index文件中的相对offset就是25-20 = 5。存储相对offset可以减小索引文件占用的空间。
- position:表示该条message在数据文件中的绝对位置。只要打开文件并移动文件指针到这个position就可以读取对应的message了。
为什么要分区
假如没有分区,topic都存在一个broker上,那么这个broker就会成为瓶颈。 Kafka中,分区可以均匀的分配在不同的broker集群上面,这样消息就可以均匀(取决于你的消息分区策略,默认的hash也会做到相对的均匀)的分布在不同的分区上,方便水平扩展。
消息如何分区
- 默认根据消息的key的hashcode%为该topic分配的分区
- 自定义分区:实现Partitioner;如果有些大客户数据(这些数据的key都是相同的),那么某个分区数据也会很多,这时候可以自定义分区,为大客户指定单独的分区。
如何设置合适的分区数量
- 主题需要达到多少吞吐量?例如是需要达到每秒钟写入100KB还是1GB
- 从单个分区读取数据的最大吞吐量是多少?每个分区都会有一个消费者,如果你知道消费者每秒将数据写入数据库的速度不会超过50MB/S,那么从一个分区读取数据的吞吐量不要超过50MB/S
- 可以通过类似的方法估算生产者向单个分区写入数据的吞吐量,不过生产者的速度一般比消费者的速度快很多,所以最好为生产者多估一些吞吐量
- 每个broker包含的分区数、可用的磁盘空间和网络带宽
- 如果消息是按照键值来分区的,那么为已有的主题新增分区就会很困难
- 单个broker对分区的数量是有限制的,因为分区越多,占用的内存越多,完成首领选举的时间就越长。