1.分区的一些基本概念规则
- 每个topic都可以被划分成一个或者多个分区(至少有一个分区),它是topic物理上的分组,在创建topic的时候指定
- 一个Partition只对应一个Broker,一个Broker可以管理多个Partition。
- 在一个分区内消息是顺序的,在不同的分区之间,kafka并不保证消息的顺序
- 同一个主题下,不同分区所包含的内容是不同的,每个消息被添加到分区当中时,会被分配一个偏移量(offset),它是消息在分区当中的唯一编号,kafka是通过offset来确保一个分区内的消息顺序的,offset的顺序并不跨越分区。
- 在需要严格保证消息的消费顺序的场景下,需要将partition数目设为1
2.分区下的Leader和Follower
- 每个分区选一个server节点作Leader,(0个或多个)server节点做Follower。
- 每个分区有且仅有一个Leader,Leader是负责当前数据读写的Partition;有(0个或多个)Follower跟随Leader,保持数据同步
- Leadre失效,会从Follower中选举一个新的Leader
- Follower挂掉、卡住或者同步太慢,Leader会把Follower删除,在新建个follower
- Leader和Follower跨节点同步,达到一种选举方式,若是在一个broker上同步没意义;每个服务器都能作为分区的一个Leader和其他分区的flowers,因此kafka是一个去中心化的集群,能被很好平衡(虽然也可以一个server上又多个leader,但是压力会大)
3.分区如何分配到broker
- 网上查到的分配策略如下
- 将所有的broker和partition排序;
- 将第i个partition分配到第(i mode n)个broker上,这个Partition的第一个Replica存在于这个分配的Broker上,并且会作为partition的优先副本
- 将第i个partition的j个副本,放到第((i+j) mode n)个broker上
- 上述做法会有问题,每一个topic的分区0都会被分配在broker 0上,第1个分区都分配到broker 1上。直到partition的id超过broker的数量后开始重复在轮询,这样会导致分区更多的在前几个broker上,这样前面的机器的压力比后面的机器压力更大,反而会造成负载不均衡。同时我们也可以做实验得到证明,真实的分配方法并不是上面这样。
- 实际上在Kafka集群中,每一个Broker都有均等分配Partition的Leader机会,kafka是先随机挑选一个broker放置分区0,然后再按顺序放置其他分区,副本也是一样的情况。第一个放置的分区副本一般都是 Leader,其余的都是 Follow 副本。
4.Segment
- 由于生产者生产的消息会不断追加到log文件末尾,为防止log文件过大导致数据定位效率低下,kafka采取了分片和索引机制。将每个partition分为多个segment file,每个segment file存着message,segment由两部分(.log数据文件 .index索引文件)组成,并且一一对应
- 目录和file都是物理存储于磁盘,但是kafka不支持随机读写,只支持顺序读写,有效提高磁盘利用率,而且顺序读写速度超过内存读写速度,所以效率很高
- partion全局的第一个segment从0开始,后续每个segment文件名为上一个segment文件最后一条消息的offset值
- ".log"数据文件
- offset在parition(分区)内的每条消息都有一个有序的id号,这个id号被称为偏移(offset),它可以唯一确定每条消息在parition(分区)内的位置。即offset表示partiion的第多少message
- MessageSize表示消息内容data的大小
- data为Message的具体内容
- ".index"索引文件
- 采取稀疏索引的方式,减少了索引文件的大小,相对于稠密索引节约了存储空间,但是查找起来更费时间
- 索引包含两个部分,分别为相对offset和position
- 消息的查找流程
- 通过offset定位数据信息在哪个文件(.log,.index)
- 找到文件后,在根据offset和文件名计算相对偏移量,可以找到index中查找到对应position????还需要试验下
5.生产者分区策略
- 每一条下消息ProducerRecord由主题名称、可选的分区号、可选的键和值组成
- 分区策略
- 如果消息ProducerRecord中指定了有效partition字段,发送记录使用该partition
- 如果消息ProducerRecord中没有指定partition字段
- 但指定key,则将使用key进行hash采用MurmurHash2算法,具备高运算性能及低碰撞率)选择一个分区
- 且没有key,则将以轮询的方式分配一个分区,常说的“round-robin”算法
- 注意:如果key不为null,那么hash计算得到的分区号会是所有分区中的任意一个;如果key为null并且有可用分区,那么计算得到的分区号仅为可用分区中的任意一个
- 自己定义分区策略
- 随机分区
- 创建一个类,并继承partitioner,改写partition
- 再修改配置文件partitioner.class=XXX._RandomPartitioner,启动即可
- 或者prop.put("partition.class",XXX._RandomPartitioner.class)
public class _RandomPartitioner implements Partitioner{ public int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster){ //获取总的分区数 Integer partitionNum = cluster.partitionsForTopic(topic); // 随机策略 int i = random.nextInt(partitionNum) return i } }
- hash分区(个人写的话可以模拟下:hash算完取绝对值,在取模)
- 轮询分区
- 分组分区
- 随机分区
提高kafka并行度,其实就是提高kafka topic分区的个数,分区个数提高了,同一时间同一消费组内可以有的消费者可以更多,消费能力增强。一般分区和消费组内的消费者保持对应