大数据面试之kafka重点

Posted 大数据小理

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了大数据面试之kafka重点相关的知识,希望对你有一定的参考价值。

Kafka怎么保证数据不丢失,不重复?
可回答:Kafka如何保证生产者不丢失数据,消费者不丢失数据?
问过的一些公司:字节跳动 x 2,快手x2,严选阿里,祖龙娱乐x2,招银网络,ebay,安恒信息参考答案:
存在数据丢失的几种情况

使用同步模式的时候,有3种状态保证消息被安全生产,在配置为1(只保证写入leader成功)的 话,如果刚好leader partition挂了,数据就会丢失。
还有一种情况可能会丢失消息,就是使用异步模式的时候,当缓冲区满了,如果配置为0(还没有 收到确认的情况下,缓冲池一满,就清空缓冲池里的消息),数据就会被立即丢弃掉。
避免方法的一些概述
1、在数据生产时避免数据丢失的方法
只要能避免上述两种情况,那么就可以保证消息不会被丢失。

  1. 在同步模式的时候,确认机制设置为-1,也就是让消息写入leader和所有的副本。
  2. 在异步模式下,如果消息发出去了,但还没有收到确认的时候,缓冲池满了,在配置文件中设置成 不限制阻塞超时的时间,也就说让生产端一直阻塞,这样也能保证数据不会丢失。
    在数据消费时,避免数据丢失的方法:如果使用了storm,要开启storm的ackfail机制;如果没有使用
    storm,确认数据被完成处理之后,再更新offset值。低级API中需要手动控制offset值。
    消息队列的问题都要从源头找问题,就是生产者是否有问题。
    讨论一种情况,如果数据发送成功,但是接受response的时候丢失了,机器重启之后就会重发。 重发很好解决,消费端增加去重表就能解决,但是如果生产者丢失了数据,问题就很麻烦了。
    2、数据重复消费的情况,处理情况如下
  3. 去重:将消息的唯一标识保存到外部介质中,每次消费处理时判断是否处理过;
  4. 不管:大数据场景中,报表系统或者日志信息丢失几条都无所谓,不会影响最终的统计分析结。
    Kafka到底会不会丢数据(data loss)? 通常不会,但有些情况下的确有可能会发生。下面的参数配置及Best practice列表可以较好地保证数据的持久性(当然是trade-off,牺牲了吞吐量)。
    如果想要高吞吐量就要能容忍偶尔的失败(重发漏发无顺序保证)。
  5. block.on.buffer.full = true
  6. acks = all
  7. retries = MAX_VALUE
  8. max.in.flight.requests.per.connection = 1
  9. 使用KafkaProducer.send(record, callback)
  10. callback逻辑中显式关闭producer:close(0)
  11. unclean.leader.election.enable=false
  12. replication.factor = 3
  13. min.insync.replicas = 2
  14. replication.factor > min.insync.replicas
  15. enable.auto.commit=false
    消息处理完成之后再提交位移
    给出列表之后,我们从两个方面来探讨一下数据为什么会丢失:

Producer端
新版本的Kafka替换了Scala版本的old producer,使用了由Java重写的producer。新版本的producer采用异步发送机制。KafkaProducer.send(ProducerRecord)方法仅仅是把这条消息放入一个缓存中(即RecordAccumulator,本质上使用了队列来缓存记录),同时后台的IO线程会不断扫描该缓存区,将满足 条件的消息封装到某个batch中然后发送出去。显然,这个过程中就有一个数据丢失的窗口:若IO线程 发送之前client端挂掉了,累积在accumulator中的数据的确有可能会丢失。
Producer的另一个问题是消息的乱序问题。假设客户端代码依次执行下面的语句将两条消息发到相同的 分区

  1. producer.send(record1);
  2. producer.send(record2);
    如果此时由于某些原因(比如瞬时的网络抖动)导致record1没有成功发送,同时Kafka又配置了重试机制和max.in.flight.requests.per.connection大于1(默认值是5,本来就是大于1的),那么重试record1成功后, record1在分区中就在record2之后,从而造成消息的乱序。很多某些要求强顺序保证的场景是不允许出 现这种情况的。发送之后重发就会丢失顺序。
    鉴于producer的这两个问题,我们应该如何规避呢??对于消息丢失的问题,很容易想到的一个方案就 是:既然异步发送有可能丢失数据, 我改成同步发送总可以吧?比如这样:

1 producer.send(record).get();
这样当然是可以的,但是性能会很差,不建议这样使用。以下的配置清单应该能够比较好地规避producer端数据丢失情况的发生:(特此说明一下,软件配置的很多决策都是trade-off,下面的配置也不 例外:应用了这些配置,你可能会发现你的producer/consumer 吞吐量会下降,这是正常的,因为你换取了更高的数据安全性)。
block.on.buffer.full = true 尽管该参数在0.9.0.0已经被标记为“deprecated”,但鉴于它的含义非常直观, 所以这里还是显式设置它为true,使得producer将一直等待缓冲区直至其变为可用。否则如果producer 生产速度过快耗尽了缓冲区,producer将抛出异常。缓冲区满了就阻塞在那,不要抛异常,也不要丢失 数据。
acks=all 很好理解,所有follower都响应了才认为消息提交成功,即"committed"。
retries = MAX 无限重试,直到你意识到出现了问题。
max.in.flight.requests.per.connection = 1 限制客户端在单个连接上能够发送的未响应请求的个数。设置此值是1表示kafka broker在响应请求之前client不能再向同一个broker发送请求。注意:设置此参数是为了避免消息乱序。
使用KafkaProducer.send(record, callback)而不是send(record)方法 自定义回调逻辑处理消息发送失败, 比如记录在日志中,用定时脚本扫描重处理。
callback逻辑中最好显式关闭producer:close(0) 注意:设置此参数是为了避免消息乱序(仅仅因为一条消息发送没收到反馈就关闭生产者,感觉代价很大)。
unclean.leader.election.enable=false 关闭unclean leader选举,即不允许非ISR中的副本被选举为leader, 以避免数据丢失。
replication.factor >= 3 参考了Hadoop及业界通用的三备份原则。
min.insync.replicas > 1 消息至少要被写入到这么多副本才算成功,也是提升数据持久性的一个参数。与acks配合使用保证replication.factor > min.insync.replicas 如果两者相等,当一个副本挂掉了分区也就没法正常工作了。通常设置replication.factor = min.insync.replicas + 1即可。

Consumer端
consumer端丢失消息的情形比较简单:如果在消息处理完成前就提交了offset,那么就有可能造成数据 的丢失。由于Kafka consumer默认是自动提交位移的,所以在后台提交位移前一定要保证消息被正常处理了,因此不建议采用很重的处理逻辑,如果处理耗时很长,则建议把逻辑放到另一个线程中去做。为 了避免数据丢失,现给出两点建议:
enable.auto.commit=false 关闭自动提交位移在消息被完整处理之后再手动提交位移

Kafka分区策略

问过的一些公司:快手,Shopee(2021.08) 参考答案:
对于消费者来说,一个consumer group中有多个consumer,一个 topic有多个partition,所以肯定会涉及到partition的分配问题,即确定每个partition由哪个consumer来消费,这就是分区分配策略(Partition Assignment Strategy)。
Kafka有三种分配策略,一是roundrobin,一是range。最新还有一个StickyAssignor策略。
1、分配分区的前提条件
首先kafka设定了默认的消费逻辑:一个分区只能被同一个消费组(ConsumerGroup)内的一个消费者消 费。
在这个消费逻辑设定下,假设目前某消费组内只有一个消费者C0,订阅了一个topic,这个topic包含6个 分区,也就是说这个消费者C0订阅了6个分区,这时候可能会发生下列三种情况:
如果这时候消费者组内新增了一个消费者C1,这个时候就需要把之前分配给C0的6个分区拿出来3
个分配给C1;
如果这时候这个topic多了一些分区,就要按照某种策略,把多出来的分区分配给C0和C1;
如果这时候C1消费者挂掉了或者退出了,不在消费者组里了,那所有的分区需要再次分配给C0。
这三种情况其实就是kafka进行分区分配的前提条件:
同一个Consumer Group 内新增消费者; 订阅的主题新增分区;
消费者离开当前所属的Consumer Group,包括shuts down 或crashes。
只有满足了这三个条件的任意一个,才会进行分区分配 。分区的所有权从一个消费者移到另一个消费者称为重新平衡(rebalance),如何rebalance就涉及到本节提到的分区分配策略。kafka提供了消费者 客户端参数partition.assignment.strategy用来设置消费者与订阅主题之间的分区分配策略。默认情况下,此参数的值为:org.apache.kafka.clients.consumer.RangeAssignor,即采用range分配策略。除此之外,Kafka中还提供了roundrobin分配策略和sticky分区分配策略。消费者客户端参数partition.asssignment.strategy可以配置多个分配策略,把它们以逗号分隔就可以了。
2、Range分配策略
Range分配策略是面向每个主题的,首先会对同一个主题里面的分区按照序号进行排序,并把消费者线 程按照字母顺序进行排序。然后用分区数除以消费者线程数量来判断每个消费者线程消费几个分区。如 果除不尽,那么前面几个消费者线程将会多消费一个分区。
我们假设有个名为T1的主题,包含了7个分区,它有两个消费者(C0和C1),其中C0的num.streams(消 费者线程) = 1,C1的num.streams = 2。排序后的分区是:0,1,2,3,4,5,6;消费者线程排序后是:C0-0,C1-0,C1-1;一共有7个分区,3个消费者线程,进行计算7/3=2…1,商为2余数为1,则每个 消费者线程消费2个分区,并且前面1个消费者线程多消费一个分区,结果会是这样的:

消费者线程 对应消费的分区序号
C0-0 0,1,2
C1-0 3,4
C1-1 5,6
这样看好像还没什么问题,但是一般在咱们实际生产环境下,会有多个主题,我们假设有3个主题
(T1,T2,T3),都有7个分区,那么按照咱们上面这种Range分配策略分配后的消费结果如下:

消费者线程 对应消费的分区序号
C0-0 T1(0,1,2),T2(0,1,2),T3(0,1,2)
C1-0 T1(3,4),T2(3,4),T3(3,4)
C1-1 T1(5,6),T2(5,6),T3(5,6)

我们可以发现,在这种情况下,C0-0消费线程要多消费3个分区,这显然是不合理的,其实这就是Range 分区分配策略的缺点。
3、RoundRobin分配策略
RoundRobin策略的原理是将消费组内所有消费者以及消费者所订阅的所有topic的partition按照字典序排 序,然后通过轮询算法逐个将分区以此分配给每个消费者。
使用RoundRobin分配策略时会出现两种情况:
如果同一消费组内,所有的消费者订阅的消息都是相同的,那么RoundRobin 策略的分区分配会是均匀的。
如果同一消费者组内,所订阅的消息是不相同的,那么在执行分区分配的时候,就不是完全的轮询 分配,有可能会导致分区分配的不均匀。如果某个消费者没有订阅消费组内的某个topic,那么在 分配分区的时候,此消费者将不会分配到这个topic 的任何分区。
分别举例说明:
第一种:比如我们有3个消费者(C0,C1,C2),都订阅了2个主题(T0 和 T1)并且每个主题都有 3 个分区(p0、p1、p2),那么所订阅的所有分区可以标识为T0p0、T0p1、T0p2、T1p0、T1p1、T1p2。此时使 用RoundRobin分配策略后,得到的分区分配结果如下:

消费者线程 对应消费的分区序号
C0 T0p0、T1p0
C1 T0p1、T1p1
C2 T0p2、T1p2
可以看到,这时候的分区分配策略是比较平均的。
第二种:比如我们依然有3个消费者(C0,C1,C2),他们合在一起订阅了 3 个主题:T0、T1 和T2(C0订阅的是主题T0,消费者C1订阅的是主题T0和T1,消费者C2订阅的是主题T0、T1和T2),这 3 个主题分别有 1、2、3 个分区(即:T0有1个分区(p0),T1有2个分区(p0、p1),T2有3个分区(p0、p1、p2)),即整个消费者所订阅的所有分区可以标识为 T0p0、T1p0、T1p1、T2p0、T2p1、T2p2。此时如果使用RoundRobin分配策略,得到的分区分配结果如下:

消费者线程 对应消费的分区序号
C0 T0p0
C1 T1p0
C2 T1p1、T2p0、T2p1、T2p2
这时候显然分配是不均匀的,因此在使用RoundRobin分配策略时,为了保证得均匀的分区分配结果,需 要满足两个条件:
同一个消费者组里的每个消费者订阅的主题必须相同;

同一个消费者组里面的所有消费者的num.streams必须相等。如果无法满足,那最好不要使用RoundRobin分配策略。
4、Sticky分配策略
Sticky分配策略,这种分配策略是在kafka的0.11.X版本才开始引入的,是目前最复杂也是最优秀的分配 策略。
Sticky分配策略的原理比较复杂,它的设计主要实现了两个目的: 分区的分配要尽可能的均匀;
分区的分配尽可能的与上次分配的保持相同。
如果这两个目的发生了冲突,优先实现第一个目的。
我们举例进行分析:比如我们有3个消费者(C0,C1,C2),都订阅了2个主题(T0 和 T1)并且每个主题都有 3 个分区(p0、p1、p2),那么所订阅的所有分区可以标识为T0p0、T0p1、T0p2、T1p0、T1p1、T1p2。此时使用Sticky分配策略后,得到的分区分配结果如下:

消费者线程 对应消费的分区序号
C0 T0p0、T1p0
C1 T0p1、T1p1
C2 T0p2、T1p2
这里我们可以发现,情况怎么和前面RoundRobin分配策略一样,其实底层实现并不一样。这里假设C2故 障退出了消费者组,然后需要对分区进行再平衡操作,如果使用的是RoundRobin分配策略,它会按照消 费者C0和C1进行重新轮询分配,再平衡后的结果如下:

消费者线程 对应消费的分区序号
C0 T0p0、T0p2、T1p1
C1 T0p1、T1p0、T1p2
但是如果使用的是Sticky分配策略,再平衡后的结果会是这样:

消费者线程 对应消费的分区序号
C0 T0p0、T1p0、T0p2
C1 T0p1、T1p1、T1p2
Stiky分配策略保留了再平衡之前的消费分配结果,并将原来消费者C2的分配结果分配给了剩余的两个消 费者C0和C1,最终C0和C1的分配还保持了均衡。这时候再体会一下sticky(翻译为:粘粘的)这个词汇 的意思,是不是豁然开朗了。
为什么要这么处理呢?
这是因为发生分区重分配后,对于同一个分区而言有可能之前的消费者和新指派的消费者不是同一个, 对于之前消费者进行到一半的处理还要在新指派的消费者中再次处理一遍,这时就会浪费系统资源。而 使用Sticky策略就可以让分配策略具备一定的“粘性”,尽可能地让前后两次分配相同,进而可以减少系 统资源的损耗以及其它异常情况的发生。
接下来,再来看一下上一节RoundRobin存在缺陷的地方,这种情况下sticky是怎么分配的?

比如我们依然有3个消费者(C0,C1,C2),他们合在一起订阅了 3 个主题:T0、T1 和 T2(C0订阅的是主题T0,消费者C1订阅的是主题T0和T1,消费者C2订阅的是主题T0、T1和T2),这 3 个主题分别有1、2、3 个分区(即:T0有1个分区(p0),T1有2个分区(p0、p1),T2有3个分区(p0、p1、p2)),即整个消费者所订阅的所有分区可以标识为 T0p0、T1p0、T1p1、T2p0、T2p1、T2p2。此时如果使用sticky分配策略,得到的分区分配结果如下:

消费者线程 对应消费的分区序号
C0 T0p0
C1 T1p0、T1p1
C2 T2p0、T2p1、T2p2
由于C0消费者没有订阅T1和T2主题,因此如上这样的分配策略已经是这个问题的最优解了!
这时候,再补充一个例子,加入C0挂了,发生再平衡后的分配结果,RoundRobin和Sticky又有什么区别 呢?
RoundRobin再平衡后的分配情况:

消费者线程 对应消费的分区序号
C1 T0p0、T1p1
C2 T1p0、T2p0、T2p1、T2p2
而如果使用Sticky策略,再平衡后分分配情况:

消费者线程 对应消费的分区序号
C1 T1p0、T1p1、T0p0
C2 T2p0、T2p1、T2p2
这里我们惊奇的发现sticky只是把之前C0消耗的T0p0分配给了C1,我们结合资源消耗来看,这相比
RoundRobin能节省更多的资源。
综上所述,建议使用sticky分区分配策略。

Kafka如何尽可能保证数据可靠性?
可回答:Kafka如何保证消费者消费数据的可靠性
问过的一些公司:星环科技,多益,ebay,招银网络x2,荣耀(2021.09) 参考答案:
1、Topic分区副本
在 Kafka 0.8.0 之前,Kafka 是没有副本的概念的,那时候人们只会用 Kafka 存储一些不重要的数据,因为没有副本,数据很可能会丢失。但是随着业务的发展,支持副本的功能越来越强烈,所以为了保证数 据的可靠性,Kafka 从 0.8.0 版本开始引入了分区副本(详情请参见 KAFKA-50)。也就是说每个分区可以人为的配置几个副本(比如创建主题的时候指定 replication-factor,也可以在 Broker 级别进行配置default.replication.factor),一般会设置为3。

Kafka 可以保证单个分区里的事件是有序的,分区可以在线(可用),也可以离线(不可用)。在众多的分区副本里面有一个副本是 Leader,其余的副本是 follower,所有的读写操作都是经过 Leader 进行的,同时 follower 会定期地去 leader 上的复制数据。当 Leader 挂了的时候,其中一个 follower 会重新成为新的 Leader。通过分区副本,引入了数据冗余,同时也提供了 Kafka 的数据可靠性。
Kafka 的分区多副本架构是 Kafka 可靠性保证的核心,把消息写入多个副本可以使 Kafka 在发生崩溃时仍能保证消息的持久性。
2、ISR机制
Kafka有两种副本数据同步策略(Kafka选择第二种)

方案 优点 缺点
半数以上完成同步,就发送ack 延迟低 选举新的leader时,容忍n台节点的故障,需要2n+1个副本
全部完成同步,才发送ack 选举新的leader时,容忍n台节点的故障,需要n+1个副本 延迟高
Kafka选择了第二种方案,原因如下:
为了容忍n台节点的故障,第一种方案需要2n+1个副本,而第二种方案只需要n+1个副本,而Kafka 的每个分区都有大量的数据,第一种方案会造成大量数据的冗余。
虽然第二种方案的网络延迟会比较高,但网络延迟对Kafka的影响较小。
为了防止Kafka在选择第二种数据同步策略时,因为某一个follower故障导致leader一直等下去,Leader维 护了一个动态的in-sync replica set (ISR)。
ISR:同步副本,和leader保持同步的follower集合。当ISR中的follower完成数据的同步之后,leader就会 给生产者发送ack。
特殊情况:
如果follower长时间未向leader同步数据,则该follower将被踢出ISR,该时间阈值由
replica.lag.time.max.ms参数设定(默认:10s)。
如果Leader发生故障,就会从ISR中选举新的leader。
3、ack应答机制
为保证producer发送的数据,能可靠的发送到指定的topic,topic的每个partition收到producer发送的数 据后,都需要向producer发送ack(acknowledgement确认收到),如果producer收到ack,就会进行下一 轮的发送,否则重新发送数据。

Kafka为用户提供了三种可靠性级别(acks参数):
acks=0
producer不等待broker的ack,broker一接收到还没有写入磁盘就已经返回。 当broker故障时,有可能丢失数据。
acks=1
producer等待broker的ack,partition的leader落盘成功后返回ack。 如果在follower同步成功之前leader故障,那么将会丢失数据。acks=-1(all)
producer等待broker的ack,partition的leader和follower(ISR中的所有follower)全部落盘成功后才 返回ack。
如果在follower同步完成后,broker发送ack之前leader发生故障,此时kafka从ISR中重新选举一个
leader,生产者没有收到ack重新发送一份到新leader上,则造成数据重复。
如果ISR中只剩一个leader时,此时leader发生故障,可能会造成数据丢失。
如果一个follower故障,该节点被踢出ISR,只要ISR中所有节点都同步即可返回ack,不影响。
4、故障处理:HW、LEO

LEO:每个副本的最后一个Offset HW:所有副本中最小的LEO

  1. follower故障
    follower发生故障后会被临时踢出ISR,待该follower恢复后,follower会读取本地磁盘记录的上次的HW, 并将log文件高于HW的部分截取掉,从HW开始向leader进行同步。等该follower的LEO大于等于该Partition的HW,即follower追上leader之后,就可以重新加入ISR了。
  2. leader故障
    leader发生故障之后,会从ISR中选出一个新的leader,之后,为保证多个副本之间的数据一致性,其余 的follower会先将各自的log文件高于HW的部分截掉,然后从新的leader同步数据。
    注意:这只能保证副本之间的数据一致性,并不能保证数据不丢失或者不重复。(是否丢数据是acks保 证)

Kafka数据丢失怎么处理?
可回答:1)Kafka的生产者写数据丢数据怎么办?2)Kafka传输过程中断电了,怎么保证可靠性? 问过的一些公司:小米,网易,严选,创略科技
参考答案:
Producer 数据不丢失:
同步模式:配置=1 (只有Leader收到,-1 所有副本成功,0 不等待)Leader Partition挂了,数据就会丢失
解决方案:设置 -1 保证produce 写入所有副本算成功 producer.type = sync request.required.acks=-1
异步模式,当缓冲区满了,如果配置为0(没有收到确认,一满就丢弃),数据立刻丢弃
解决方案:不限制阻塞超时时间。就是一满生产者就阻塞

  1. producer.type = async
  2. request.required.acks=1
  3. queue.buffering.max.ms=5000
  4. queue.buffering.max.messages=10000
  5. queue.enqueue.timeout.ms = -1
  6. batch.num.messages=200
    Customer 不丢失数据
    在获取kafka的消息后正准备入库(未入库),但是消费者挂了,那么如果让kafka自动去维护offset ,它就会认为这条数据已经被消费了,那么会造成数据丢失。
    解决方案:使用kafka低级API,自己手动维护偏移量,当数据入库之后进行偏移量的更新(适用于基本 数据源)
    流处理中的几种可靠性语义:
    at most once 每条数据最多被处理一次(0次或1次),会出现数据丢失的问题
    at least once 每条数据最少被处理一次(1次或更多),这个不会出现数据丢失,但是会出现数据重复
    exactly once 每种数据只会被处理一次,没有数据丢失,没有数据重复,这种语义是大家最想实现的,也是最难实现的
    但是开启WAL后,依旧存在数据丢失问题,原因是任务中断时receiver也被强行终止了,将会造成数据丢 失。在Streaming程序的最后添加代码,只有在确认所有receiver都关闭的情况下才终止程序。

以上是关于大数据面试之kafka重点的主要内容,如果未能解决你的问题,请参考以下文章

大数据面试葵花宝典之Kafka进阶

大数据面试之hive重点

大数据之Zookeeper:Zookeeper选举机制(面试重点)

大数据之Kafka看这一篇就够了

大数据技术之_05_Hadoop学习_02_MapReduce_MapReduce框架原理+InputFormat数据输入+MapReduce工作流程(面试重点)+Shuffle机制(面试重点)(示例

大数据技术之KafkaKafka APIKafka监控Flume对接KafkaKafka面试题