生产者分区写入规则
概述
生产者写入消息到topic,Kafka将依据不同的规则将数据分配到不同的分区中,如果指定了分区数据就会写到指定分区,如果没有指定分区,会按照下列若干规则进行指定分区:
分类
Hash分区(指定Key默认开启)
优点:相同的key会进去同一分区
缺点:由于相同key的Hash取余结果是相同的,会导致数据倾斜问题
轮询分区(没有指定Key时开启)
- 默认的策略,也是使用最多的策略,可以最大限度保证所有消息平均分配到一个分区
- 如果在生产消息时,key为null,则使用轮询算法均衡地分配分区
优点:数据分配更加均匀
缺点:相同key的数据进去不同的分区
- Kafka生产者写入数据之前:先将数据写入缓存中,与分区构建一个连接,发送一个批次的数据
- 第一条数据:先构建0分区的连接,第二条不是0分区的,所以直接构建一个批次,发送第一条
- 第二条数据:先构建1分区的连接,第三条不是1分区的,所以直接构建一个批次,发送第二条
- ……
- 每条数据需要构建一个批次,9条数据,9个批次,每个批次一条数据
- 批次多,每个批次数据量少,性能比较差
优化缺点:增加每个批次的数据量,从而介绍批次数,这样性能较好,于是就有了黏性分区
在2.x之前:轮询分区,2.x之后:由于轮询分区性能太差,使用黏性分区
黏性分区(在2.X之后,数据没有key的默认分区规则)
设计:让数据尽量的更加均衡,实现少批次多数据
规则:
- 第一次:将所有数据随机选择一个分区,全部写入这个分区中,将这次的分区编号放入缓存中
- 第二次开始根据缓存中是否有上一次的编号
有:直接使用上一次的编号
如果没有:重新随机选择一个
随机分区(不用)
随机策略,每次都随机地将消息分配到每个分区。在较早的版本,默认的分区策略就是随机策略,也是为了将消息均衡地写入到每个分区。但后续轮询策略表现更佳,所以基本上很少会使用随机策略。
自定义分区
实现代码:
1.创建自定义分区器
import org.apache.kafka.clients.producer.Partitioner;
import org.apache.kafka.common.Cluster;
import java.util.Map;
import java.util.Random;
/**
* @ClassName UserPartition
* @Description TODO 自定义分区器,实现随机分区
* @Date 2021/3/31 9:21
* @Create By Frank
*/
public class UserPartition implements Partitioner {
/**
* 返回这条数据对应的分区编号
* @param topic:Topic的名
* @param key:key的值
* @param keyBytes:key的字节
* @param value:value的值
* @param valueBytes:value的字节
* @param cluster:集群对象
* @return
*/
@Override
public int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster) {
//获取Topic的分区数
Integer count = cluster.partitionCountForTopic(topic);
//构建一个随机对象
Random random = new Random();
//随机得到一个分区编号
int part = random.nextInt(count);
return part;
}
@Override
public void close() {
//释放资源
}
@Override
public void configure(Map<String, ?> configs) {
//获取配置
}
}
2.在Kafka生产者配置中,自定使用自定义分区器的类名
props.put(ProducerConfig.PARTITIONER_CLASS_CONFIG, 类所在包路径.UserPartition.class);
副本机制(保证数据安全)
概述
副本就是给数据做备份,当Kafka集群中的某个broker的数据(分区)丢失或者宕机,在其他broker中的副本是可用的
- 为了保证安全和写的性能:划分了副本角色
- leader副本:对外提供读写数据
- follower副本:与Leader同步数据,如果leader故障,选举一个新的leader
查看某个topic的副本情况
bin/kafka-topics.sh --zookeeper 主机名:2181 --describe --topic 主题名
Topic: test PartitionCount: 1 ReplicationFactor: 1 Configs:
Topic: test Partition: 0 Leader: 2 Replicas: 2 Isr: 2
Kafka分区副本概念:AR、ISR、OSR
- AR:All - Replicas
- 所有副本:指的是一个分区在所有节点上的所有副本
0分区有1个副本在第三台机器上
Partition: 0 Leader: 2 Replicas: 2
- ISR:In - Sync - Replicas
- 可用副本:Leader与所有正在与Leader同步的Follower副本
Partition: 0 Leader: 2 Replicas: 2 Isr: 2
- 列表中:按照优先级排列【Controller根据副本同步状态以及Broker健康状态】,越靠前越会成为leader.
- Out - Sync - Replicas
- 不可用副本:与Leader副本的同步差距很大,成为一个OSR列表的不可用副本
- 原因:网路故障等外部环境因素,某个副本与Leader副本的数据差异性很大
- 判断是否是一个OSR副本?
0.9之前:时间和数据差异
replica.lag.time.max.ms = 10000 可用副本的同步超时时间
replica.lag.max.messages = 4000 可用副本的同步记录差,该参数在0.9以后被删除
0.9以后:只按照时间来判断
replica.lag.time.max.ms = 10000 可用副本的同步超时时间
producer的ACKS参数
ACKS参数表示生产者生产消息,对写入副本的要求严格程度,不同的ACKS参数性能不同,安全性也不同
参数分类
acks配置为0:
acks=0:生产者不管kafka集群有没有收到,直接发送下一条消息
优缺点:
优点:性能好,就是快
缺点:容易导致数据丢失,概率比较高
acks配置为1:
acks=1:生产者将数据发送给Kafka,Kafka等待这个分区leader副本写入成功,返回ack确认,生产者发送下一条
优缺点:
优点:性能和安全上做了平衡
缺点:依旧存在数据丢失的概率,但概率比较小
acks配置为-1或者all
acks=all/-1:生产者将数据发送给Kafka,Kafka等待这个分区所有副本全部写入,返回ack确认,生产者发送下一条
优缺点:
优点:数据安全
缺点:慢
补充:如果Kafka没有返回acks怎么办?
- 生产者会等待Kafka集群返回ACKS,所有会有一个等待时间,如果Kafka在规定的时候内没有返回ACKS,代表数据丢失
- 生产者有重试机制,重新发送这条数据给Kafka
存在的问题:acks每次在Kafka集群数据写入成功时返回,如果在返回的过程中Kafka宕机,就会导致数据重复,如何解决?