Questions

Kafka吞吐量大的原因

  • 磁盘顺序读写

  • page cache:落盘时,先落内存缓冲区,再写入硬盘

  • 分区分段 + 索引(分 topicbrokersegment

  • 高速传输,零拷贝(落盘使用 mmap,读取使用 sendfile

  • 批量读写

  • 压缩

Kafka 是 CP 还是 AP?

kakfa 依赖 zkzkCP,选主期间集群不可用,kafka 自然也是 CP

Kaka 为什么不采用 B+树 索引?

应用场景不同所决定的。

MySQL 中需要频繁地执行 CRUD 的操作,CRUDMySQL 的主要工作内容,而为了支撑这个操作需要使用维护量大很多的 B+树去支撑。B+树中数据有插入、更新、删除的时候都需要更新索引,还会引来页分裂等相对耗时的操作。

Kafka 中的索引文件是顺序追加文件的操作,和 B+树比起来工作量要小很多。Kafka 中的消息一般都是顺序写入磁盘,再到从磁盘顺序读出(不深入探讨page cache等),他的主要工作内容就是:写入+读取,没有更新索引的操作

换句话说,检索查询只是Kafka的一个辅助功能,不需要为了这个功能而去花费特别太的代价去维护一个高level的索引。

如何保证数据能读成功 / 写成功?

  • 生产者:通过 ISR(In-Sync Replicas)机制,设置至少一个副本,数据写入主节点和至少 1follower 后才视为成功,否则重试

什么情况下出现数据丢失?

  • 生产者丢数据

    • 网络问题,没发出去。可以在发送后增加回调,如果回调时发现写入失败可以尝试重写

    • 如果选择异步发送,那么实际发出前消息会存在缓冲区中。此时如果生产者挂了,消息就丢了。

    • 和你的 ISR 设置有关:

      • 如果 ack 参数设的 0,即不管写入结果,则只要服务端写消息时出现任何问题,都会导致消息丢失

      • 如果 ack 设的 1,即只写入 leader 就视为成功;或者 ack > 1 但 副本书为 0;或者配置成了 leader 挂掉时允许非 ISR 升为 leader

        • leader 挂会导致丢失数据

      • 可以通过 acks=-1/all 设置为必须等到所有分区都写入完成才认为发送完成,这样只要有一个分区存活就能保证数据不丢失,但是效率最低

  • broker 丢数据

    • kafka 通过 Page Cache 将数据写入磁盘,也是先将数据流写入缓存中,但是什么时候将缓存的数据写入文件中是由操作系统自行决定。期间如果宕机会丢失缓存文件。

  • 接收者丢数据

    • 自动提交 offset:每隔一定的时间间隔,将收到的消息进行 commit,和消费消息的过程是异步的。就可能消费失败,但自动 commit

    • 手动提交 offset :如果业务客户端的 bug,在收完消息前就提交了 offset,那么就可能造成数据的丢失

重复消费的情况

  • 生产者重复生产

    • 原因:生产者消息发送后broker收到消息,但是生产者未收到broker的响应,触发重试机制导致消息重复

    • 解决方案:配置enable.idempotence=true开启broker接收消息的幂等性

    • 原理:在初始化生产者时分配一个唯一的pid,每次提交都会递增一个sequence number(从0开始),发送消息时会携带这两个信息,broker记录值并在每次收到消息时比对,如果相同则拒绝接收

  • 消费者重复消费

    • 原因:消费者消费完成后手动提交offset,此时因为异常未能提交成功(自己挂了,或者网络异常),之后又从原来的offset开始消费

    • 如果消息处理时间过长,会导致消费者被判断为宕机,进而被踢出消费者组,触发 rebanlance。于是别的机器也可能重复消费到消息

    • 解决方案:

      • 分布式锁:如在消费开始之前先将分区和offset值作为唯一key写缓存(最好设置过期时间),消费完成并提交offset后将key删除

      • 业务上使用唯一键保证幂等性

消息积压怎么办?

先解释:消息堆积是指在消息队列中,由于某些原因(例如消费者处理能力不足、消费者宕机、网络故障等),消息的生产速度大于消费速度,导致队列中积压了大量的消息。这会导致队列的长度不断增加,最终可能会影响系统的稳定性和可用性。

  1. 优化消费者处理速度:分析消费者处理逻辑的性能瓶颈,进行优化

  2. 消费者消费异步化,起新协程处理

  3. 增加消费者机器

  4. 增加队列容量,在网络故障时存储更多的消息

  5. 增加积压监控和报警,发生积压时及时处理

怎么保证数据顺序?

写到同一个 partition 下。同一分区下的消息是有序的

为什么不用redis做消息队列?

  1. 最大的问题:浪费成本。现在都在降本增效,内存资源贵且容量小,适合存频繁读写的数据。消息队列这种大概率只生产消费一次数据的场景,用内存实在浪费

  2. 持久化不够可靠

  • AOF 持久化配置为每秒写盘,但这个写盘过程是异步的,Redis 宕机时会存在数据丢失的可能

  • 主从复制也是异步的,主从切换时,也存在丢失数据的可能(从库还未同步完成主库发来的数据,就被提成主库)

  1. 其他具体使用上的不方便:

    • list 会有 热 key、不支持确认机制(客户端收到消息后未处理就宕机,则消息就丢失了)、不支持多订阅者的问题

    • pub/sub 模式下数据不会不会写入 RDB、AOF 中,存在数据丢失问题,也不支持确认机制

    • stream 会把数据存在缓冲区,超过缓冲区后,数据会被丢弃

生产者发送消息有哪些模式

  • 发后即忘 fire-and-forget

    • 只管往 kafka 里面发消息,但是不关心消息是否正确到达。效率最高,但是可靠性也最差

  • 同步 sync

  • 异步 async

    • producer.send() 传入一个回调函数,消息不管成功或者失败都会调用这个回调函数,这样就是异步发送

    • 在回调函数中选择记录日志还是重试都取决于调用方

Kafka 支持读写分离吗

Kafka 是不支持读写分离的。读写分离的好处主要就是让一个节点去承担另一个节点的负载压力,也就是能做到一定程度的负载均衡,而 kafka 不通过读写分离也可以一定程度上去实现负载均衡(通过 partition 分成多片)。而且对于 Kafka 的架构来说,读写分离有两个很大的缺点:数据不一致和时延问题

  1. 数据不一致的问题:读写分离必然涉及到数据的同步,只要是不同节点之间的数据同步,必然会有数据不一致的问题存在。

  2. 延时问题:由于 Kafka 独特的数据处理方式,导致如果将数据从一个节点同步到另一个节点必然会经过主节点磁盘和从节点磁盘,对一些延时性要求较高的应用来说,并不太适用

参考

Java技术那些事 - Kafka 会不会丢消息?怎么处理的?

Magic Kaito - 选 Redis 还是 MQ

Last updated