zl程序教程

您现在的位置是:首页 >  数据库

当前栏目

大数据面试重点之kafka(三)

2023-04-18 16:27:53 时间

Kafka如何保证全局有序?
可回答:1)Kafka消费者怎么保证有序性?2)Kafka生产者写入数据怎么保证有序?3)Kafka可以保证 数据的局部有序,如何保证数据的全局有序?4)Kafka消息的有序性
问过的一些公司:快手x3,360x2,安恒信息,京东,京东(2021.07),重庆富民银行(2021.09) 参考答案:
1、设置Key值,指定分区
kafka分区是存在K和V的,K就是分区,一般都是默认的,而默认的经常会发生一些我们并不像看到的结 果,例如对同一数据进行多次操作不同分区会导致后进入先出,这就是因为跨分区导致的结果,因此我 们要设置key用来进行hash取模来确定分区,并且,这个再kafka源码是存在的,就在DefaultPartitioner.java中。
在这里插入图片描述
通过源码我们可以发现,再源码中默认的key为空,则系统会运算出一个partition,如果用这种方式,那 么,就会导致分区内有序而分区无序,会导致数据无序,因此,要指定一个key值,也就是指定分区, 这样的话,同一个数据发送到同一个分区,而多个分区依旧可以并行,同时实现数据有序和多分区并 行。
如果数据是从MySQL传过来的话,一般来说都是会有自增长主键的,我们可以直接读取主键来作为运算 的key值进行操作,这样就可以保证数据的操作唯一性了。
2、 max.in.flight.requests.per.connection 设置为1
max.in.flight.requests.per.connection 设置为1,每个批次里面一次只写入一条消息到kafka里面,而这个参数,在kafka官网中是有说明的:
在这里插入图片描述
官网已经说的很明显了,如果设置打于1,那么每次重试可能存在从新排序(乱序)的情况,比如我设 置为5,那么我传输的过程应该是1,2,3,4,5。
但是如果网络波动导致3丢失,那么kafka自动重试只会拉取3,也就是排序就成了1,2,4,5,3。 所以,记得将每个批次的消息数设置为1,防止出现重试乱序的情况。
3、设置重试次数大于100次
每一次kafka操作为了防止网络延时等问题,要设置重试次数大于100次

生产者消费者模式与发布订阅模式有何异同?
问过的一些公司:百度

参考答案:
1、点对点模式(一对一,消费者主动拉取数据,消息收到后消息清除)
消息生产者生产消息发送到Queue中,然后消息消费者从Queue中取出并且消费消息。
消息被消费以后,Queue中不再有存储,所以消息消费者不可能消费到已经被消费的消息。Queue支持 存在多个消费者,但是对一个消息而言,只会有一个消费者可以消费。
在这里插入图片描述
2、发布/订阅模式(一对多,消费者消费数据之后不会清除消息)
消息生产者(发布)将消息发布到topic中,同时有多个消息消费者(订阅)消费该消息。和点对点模式 不同,发布到topic的消息会被所有订阅者消费。
在这里插入图片描述
Kafka的消费者组是如何消费数据的
问过的一些公司:京东参考答案:
Consumer Group (CG)
消费者组,由多个consumer组成。消费者组内每个消费者负责消费不同分区的数据,一个分区只能由一 个消费者消费;消费者组之间互不影响。所有的消费者都属于某个消费者组,即消费者组是逻辑上的一 个订阅者。

Kafka的oGset管理
问过的一些公司:招银网络参考答案:

消费者在消费的过程中需要记录自己消费了多少数据,即消费 Offset。Kafka Offset 是Consumer Position,与 Broker 和 Producer 都无关。每个 Consumer Group、每个 Topic 的每个Partition 都有各自的Offset,如下图所示。

在这里插入图片描述
通常有如下几种 Kafka Offset 的管理方式:
Spark Checkpoint:在Spark Streaming 执行Checkpoint 操作时,将Kafka Offset 一并保存到HDFS 中。这种方式的问题在于:当Spark Streaming 应用升级或更新时,以及当Spark 本身更新时, Checkpoint 可能无法恢复。因而,不推荐采用这种方式。
HBASE、Redis 等外部NOSQL 数据库:这一方式可以支持大吞吐量的Offset 更新,但它最大的问题在于:用户需要自行编写HBASE 或Redis 的读写程序,并且需要维护一个额外的组件。ZOOKEEPER:老版本的位移offset是提交到zookeeper中的,目录结构是
: /consumers/<group.id>/offsets/ / ,但是由于ZOOKEEPER 的写入能力并不会随着ZOOKEEPER 节点数量的增加而扩大,因而,当存在频繁的Offset 更新时, ZOOKEEPER 集群本身可能成为瓶颈。因而,不推荐采用这种方式。
Kafka自身的一个特殊Topic( consumer_offsets)中:这种方式支持大吞吐量的Offset更新,又不需要手动编写Offset 管理程序或者维护一套额外的集群,因而是迄今为止最为理想的一种实现方式。

在这里插入图片描述
另外几个与 Kafka Offset 管理相关的要点如下:
Kafka 默认是定期帮你自动提交位移的(enable.auto.commit=true)。有时候,我们需要采用自己来管理位移提交,这时候需要设置 enable.auto.commit=false。
属性 auto.offset.reset 值含义解释如下:
earliest :当各分区下有已提交的Offset 时,从“提交的Offset”开始消费;无提交的Offset 时, 从头开始消费;
latest : 当各分区下有已提交的Offse`t 时,从提交的Offset 开始消费;无提交的Offset时,消费新产生的该分区下的数据;
none : Topic 各分区都存在已提交的Offset 时,从Offset 后开始消费;只要有一个分区不存在已提交的Offset,则抛出异常。

  1. kafka-0.10.1.X版本之前: auto.offset.reset 的值为smallest,和,largest.(offest保存在zk 中);
  2. kafka-0.10.1.X版本之后: auto.offset.reset 的值更改为:earliest,latest,和none (offest保存在kafka的一个特殊的topic名为: consumer_offsets里面);

Kafka为什么同一个消费者组的消费者不能消费相同的分区?
问过的一些公司:网易参考答案:
Kafka通过消费者组机制同时实现了发布/订阅模型和点对点模型。多个组的消费者消费同一个分区属于 多订阅者的模式,自然没有什么问题;而在单个组内某分区只交由一个消费者处理的做法则属于点对点 模式。其实这就是设计上的一种取舍,如果Kafka真的允许组内多个消费者消费同一个分区,也不是什 么灾难性的事情,只是没什么意义,而且还会重复消费消息。
通常情况下,我们还是希望一个组内所有消费者能够分担负载,让彼此做的事情没有交集,做一些重复 性的劳动纯属浪费资源。就如同电话客服系统,每个客户来电只由一位客服人员响应。那么请问我就是 想让多个人同时接可不可以?当然也可以了,技术上没什么困难,只是这么做没有任何意义罢了,既拉 低了整体的处理能力,也造成了人力成本的浪费。
总之,这种设计不是出于技术上的考量而更多还是看效率等非技术方面。

如果有一条oGset对应的数据,消费完成之后,手动提交失败,如何处理?
问过的一些公司:安恒信息参考答案:
回滚,利用Kafka的事务解决。

正在消费一条数据,Kafka挂了,重启以后,消费的oGset是哪一个
问过的一些公司:招银网络参考答案:

可以在Java API中设置 auto.offset.reset 的值(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG)来进行指定。
先来看几个测试实验,结论在最后会给出
auto.oGset.reset值含义解释
earliest :当各分区下有已提交的offset时,从提交的offset开始消费;无提交的offset时,从头开始消费
:当各分区下有已提交的offset时,从提交的offset开始消费;无提交的offset时,消费新产生的 该分区下的数据
latest
none :topic各分区都存在已提交的offset时,从offset后开始消费;只要有一个分区不存在已提交的
offset,则抛出异常
案例测试如下:
1、同分组测试

  1. 测试一
    测试环境:Topic为lsztopic7,并生产30条信息。lsztopic7详情:

在这里插入图片描述
创建组为“testtopi7”的consumer,将enable.auto.commit设置为false,不提交offset。依次更改
auto.offset.reset的值。此时查看offset情况为:
在这里插入图片描述
![earliest:客户端读取30条信息,且各分区的offset从0开始消费。latest: 客 户 端 读 取 0 条 信 息 。 none:抛出NoOffsetForPartitionException异常。
测试结论:

  1. 新建一个同组名的消费者时,auto.offset.reset值含义:
  2. earliest 每个分区是从头开始消费的。
  3. none 没有为消费者组找到先前的offset值时,抛出异常
  4. 测试二
    测试环境:测试场景一下latest时未接受到数据,保证该消费者在启动状态,使用生产者继续生产10条数 据,总数据为40条。
    在这里插入图片描述
    测试结果:

latest:客户端取到了后生产的10条数据
测试结论:

1 当创建一个新分组的消费者时,auto.offset.reset值为latest时,表示消费新的数据(从consumer创建 开始,后生产的数据),之前产生的数据不消费。

  1. 测试三
    测试环境:在测试环境二,总数为40条,无消费情况下,消费一批数据。运行消费者消费程序后,取到
    5条数据。 即,总数为40条,已消费5条,剩余35条。

测试结果:
earliest:消费35条数据,即将剩余的全部数据消费完。
latest:

  1. 消费9条数据,都是分区3的值。
  2. offset:0 partition:3
  3. offset:1 partition:3
  4. offset:2 partition:3
  5. offset:3 partition:3
  6. offset:4 partition:3
  7. offset:5 partition:3
  8. offset:6 partition:3
  9. offset:7 partition:3
  10. offset:8 partition:3
    none:抛出NoOffsetForPartitionException异常

测试结论:

  1. earliest:当分区下有已提交的offset时,从提交的offset开始消费;无提交的offset时,从头开始消 费。

  2. latest:当分区下有已提交的offset时,从提交的offset开始消费;无提交的offset时,消费新产生的该 分区下的数据。

  3. none:当该topic下所有分区中存在未提交的offset时,抛出异常。

  4. 测试四
    测试环境:再测试三的基础上,将数据消费完,再生产10条数据,确保每个分区上都有已提交的
    offset。此时,总数为50,已消费40,剩余10条
    在这里插入图片描述
    测试结果:
    none:

  5. 消费10条信息,且各分区都是从offset开始消费

  6. offset:9 partition:3

  7. offset:10 partition:3

  8. offset:11 partition:3

  9. offset:15 partition:0

  10. offset:16 partition:0

  11. offset:17 partition:0

  12. offset:18 partition:0

  13. offset:19 partition:0

  14. offset:20 partition:0

  15. offset:5 partition:2
    测试结论:

1 值为none时,topic各分区都存在已提交的offset时,从offset后开始消费;只要有一个分区不存在已提交 的offset,则抛出异常。
2、不同分组下测试
1)测试五
测试环境:在测试四环境的基础上:总数为50,已消费40,剩余10条,创建不同组的消费者,组名为
testother7。
在这里插入图片描述
测试结果:
earliest:消费50条数据,即将全部数据消费完。
latest:消费0条数据。
none:抛出异常

测试结论:

  1. 组与组间的消费者是没有关系的。
  2. topic中已有分组消费数据,新建其他分组ID的消费者时,之前分组提交的offset对新建的分组消费不起作 用。
    3、总结
    在这里插入图片描述
    从上面的实验来看,基本上对auto.offset.reset不同的值进行了测试根据下面的实验数据来进行分析
    CURRENT-OFFSET:表示消费者消费了数据之后提交的offset,即消费者消费了的数据的偏移量。如果为unknown,则表示消费者未提交过offset。
    LOG-END-OFFSET:表示的是该分区的HW。

LAG:表示延迟滞后,也就是生产者已经写到kafka集群了,然后有还没有被消费的数量,是logSize- currentOffset,logsize指的是消息总数。
得出以下的一些结论:

  1. 如果CURRENT-OFFSET不是为unknown(消费者以前消费过数据,提交过offset),重启消费者时
    earliest、latest、none都是会从CURRENT-OFFSET一直消费到LOG-END-OFFSET。也就是不会更新offset。
  2. 如果CURRENT-OFFSET为unknown,重启消费者时earliest、latest、none才会展现出他们各自的不同:
    earliest:会从该分区当前最开始的offset消息开始消费(即从头消费),如果最开始的消息offset是0,那么消费者的offset就会被更新为0. latest:只消费当前消费者启动完成后生产者新生产的数据。旧数据不会再消费。offset被重置为分 区的HW。
    none:启动消费者时,该消费者所消费的主题的分区没有被消费过,就会抛异常。(一般新建主题 或者用新的消费者组是使用这个就会抛异常。。宕机重启的话,使用这个就没问题。。这个的作用 是什么?我猜测应该是用于在重启消费者时检查该消费者所消费的主题以及所属的消费者组的名称 是否写错了,导致该消费者没有消费原来主题分区)
    这里再强调一遍:
    在Java API中设置auto.offset.reset值(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG)。

Kafka支持什么语义,怎么实现Exactly Once?
可回答:
问过的一些公司:网易,快手 x 2
参考答案:
Kafka支持的消费语义有以下几种:
at most once:最多消费一次,消息可能会丢失 log日志
at least once:至少消费一次,但是会重复消费例如手动异步提交offset exactly once:正好一次,不丢失,不重复

  1. 至少一次:at-least-once 表示的是关闭offset自动提交功能,消费端在消费数据的时候很可能在commitAync之前,已经保存在数据库,但是这个时候服务器宕机了,从而导致offset不能提交成功。这 个时候再次启动消费者的时候,还是会再次写入数据库,也就是至少一次会重复消费,至少不会丢数 据。
  2. 至多一次:at-most-once 表示有可能是0次或者1次,可以选择开启自动提交offset的功能,然后把自动提交offset的时间设置一下,有可能消费者在消费的时间段内就到了自动提交的时间,从而导致了 offset已经提交了,但是数据库保存还没进行。下一次再消费的时候就会认为offset已经成功了,直接丢 弃消息。就会造成丢数据。
  3. 仅一次:exactly-once 表示数据仅被消费一次,还是开启自动开启offset提交的功能,可以开启consumer.seek()方法,相当于自己处理分区和offset,可以在此基础上开启事务,保持原子性,只有数据 库保存成功再提交offset,保证两者同时成功。
    Exactly Once语义
    将服务器的ACK级别设置为-1,可以保证Producer到Server之间不会丢失数据,即At Least Once语义。相对的,将服务器ACK级别设置为0,可以保证生产者每条消息只会被发送一次,即At Most Once语义。

At Least Once可以保证数据不丢失,但是不能保证数据不重复;相对的,At Least Once可以保证数据不重复,但是不能保证数据不丢失。但是,对于一些非常重要的信息,比如说交易数据,下游数据消费者 要求数据既不重复也不丢失,即Exactly Once语义。在0.11版本以前的Kafka,对此是无能为力的,只能保证数据不丢失,再在下游消费者对数据做全局去重。对于多个下游应用的情况,每个都需要单独做全 局去重,这就对性能造成了很大影响。
0.11版本的Kafka,引入了一项重大特性:幂等性。所谓的幂等性就是指Producer不论向Server发送多少 次重复数据,Server端都只会持久化一条。幂等性结合At Least Once语义,就构成了Kafka的Exactly Once语义。即:
At Least Once + 幂等性 = Exactly Once
要启用幂等性,只需要将Producer的参数中enable.idompotence设置为true即可。Kafka的幂等性实现其 实就是将原来下游需要做的去重放在了数据上游。开启幂等性的Producer在初始化的时候会被分配一个PID,发往同一Partition的消息会附带Sequence Number。而Broker端会对<PID, Partition, SeqNumber>做缓存,当具有相同主键的消息提交时,Broker只会持久化一条。
但是PID重启就会变化,同时不同的Partition也具有不同主键,所以幂等性无法保证跨分区跨会话的
Exactly Once。

Kafka的消费者和消费者组有什么区别?为什么需要消费者组?
可回答:说下Kafka的消费者和消费者组,以及它们的作用是什么? 问过的一些公司:字节,电信云计算,京东,拼多多,bigo
参考答案:
1、什么是消费者
顾名思义,消费者就是从kafka集群消费数据的客户端,如下图,展示了一个消费者从一个topic中消费 数据的模型
在这里插入图片描述
2、为什么需要消费者组
如果这个时候 kafka 上游生产的数据很快,超过了这个 消费者1 的消费速度,那么就会导致数据堆积, 产生一些大家都知道的蛋疼事情了,那么我们只能加强 消费者 的消费能力,所以也就有了
消费者

组 。
3、什么是消费者组
所谓 消费者组 ,其实就是一组 消费者 的集合,当我们看到下面这张图是不是就特别舒服了,我们采 用了一个 消费组 来消费这个 topic ,众人拾柴火焰高,其消费能力那是按倍数递增的,所以这里我们
单消费者

一般来说都是采用 消费者组 来消费数据,而不会是
注意:
来消费数据的。

一个topic可以被多个消费者组消费,但是每个消费者组消费的数据是互不干扰的,也就是说,每个消费 组消费的都是完整的数据 。
一个分区只能被同一个消费组内的一个消费者消费,而不能拆给多个消费者消费,也就是说如果你某个 消费者组内的消费者数比该 Topic 的分区数还多,那么多余的消费者是不起作用的
在这里插入图片描述
扩展一下:

  1. 是不是一个消费组的消费者越多其消费能力就越强呢?
    从下图我们就可以很好的可以回答这个问题了,我们可以看到消费者4是完全没有消费任何的数据的, 所以如果你想要加强消费者组的能力,除了添加消费者,分区的数量也是需要跟着增加的,只有这样他 们的并行度才能上的去,消费能力才会强。
    在这里插入图片描述
    为了提高消费组的消费能力,我是不是可以随便添加分区和消费者呢?
    答案当然是否定的。。。我们看到下图,一般来说我们建议消费者数量和分区数量是一致的,当我们的 消费能力不够时,就必须通过调整分区的数量来提高并行度,但是,我们应该尽量来避免这种情况发 生。
    比如:现在我们需要在下图的基础上增加一个分区4,那么这个分区4该由谁来消费呢?这个时候kafka会 进行分区再均衡,来为这个分区分配消费者,分区再均衡期间该Topic是不可用的,并且作为一个被消费 者,分区数的改动将影响到每一个消费者组 ,所以在创建 topic 的时候,我们就应该考虑好分区数,来尽量避免这种情况发生。

在这里插入图片描述
Kafka producer的写入数据过程?
问过的一些公司:流利说参考答案:
Kafka的Producer发送消息采用的是异步发送的方式。在消息发送的过程中,涉及到了两个线程——main 线程和Sender线程,以及一个线程共享变量——RecordAccumulator。main线程将消息发送给RecordAccumulator,Sender线程不断从RecordAccumulator中拉取消息发送到Kafka broker。
在这里插入图片描述

相关参数
batch.size:只有数据积累到batch.size之后,sender才会发送数据。 linger.ms:如果数据迟迟未达到batch.size,sender等待linger.time之后就会发送数据。