每日八股文8.27

每日八股-8.27

  • Kafka
    • [1.Kafka 是什么?主要应用场景有哪些?](#1.Kafka 是什么?主要应用场景有哪些?)
    • [2.Producer / Consumer / Broker / Topic / Partition 基本概念](#2.Producer / Consumer / Broker / Topic / Partition 基本概念)
    • [3.Kafka 的多副本机制了解吗?](#3.Kafka 的多副本机制了解吗?)
    • [4.Kafka 如何保证消息不丢失、不重复、不乱序?](#4.Kafka 如何保证消息不丢失、不重复、不乱序?)
    • [5.Kafka 如何保证消息不重复?](#5.Kafka 如何保证消息不重复?)
    • [6.Kafka 如何保证消息不乱序?](#6.Kafka 如何保证消息不乱序?)
    • [7.Zookeeper 在 Kafka 中的作用](#7.Zookeeper 在 Kafka 中的作用)
    • [8.Kafka 高效文件存储设计](#8.Kafka 高效文件存储设计)
    • [9.Partition 数据如何保存顺序](#9.Partition 数据如何保存顺序)
    • [10.Kafka 消费模式](#10.Kafka 消费模式)
    • [11. Kafka 系统工具](#11. Kafka 系统工具)

Kafka

1.Kafka 是什么?主要应用场景有哪些?

Kafka 是一个分布式的流式数据平台,最常见的用途是作为高性能消息队列中间件。

它有三个核心功能:

  1. 消息队列功能
    支持发布(Producer)和订阅(Consumer)消息流,解耦系统之间的数据传递。
    类似 RabbitMQ、ActiveMQ,但吞吐量更高、可扩展性更好。
  2. 持久化存储
    消息会写入磁盘,并且有多副本机制,保证即使机器宕机也不会丢数据。
  3. 流式处理能力
    内置 Kafka Streams API,可以在数据流经过时直接进行实时计算和处理(比如实时统计、过滤、聚合)。

主要应用场景

  1. 消息队列(最常见)
    在不同系统或服务之间传递数据,比如订单系统 → 库存系统。
  2. 实时数据管道
    收集日志、监控数据、传感器数据等,实时传输到数据仓库或分析系统。
  3. 实时流处理
    对数据流进行实时计算,比如实时推荐、实时风控。

2.Producer / Consumer / Broker / Topic / Partition 基本概念


Kafka 将生产者发布的消息发送到 Topic(主题)中,需要这些消息的消费者可以订阅这些 Topic(主题)。Kafka 比较重要的几个概念:

  • Producer(生产者): 产生消息的一方。
  • Consumer(消费者): 消费消息的一方。
  • Broker(代理): 可以看作是一个独立的 Kafka 实例。多个 Kafka Broker 组成一个 Kafka Cluster。
  • Topic(主题): Producer 将消息发送到特定的主题,Consumer 通过订阅特定的 Topic(主题)来消费消息。
  • Partition(分区): Partition 属于 Topic 的一部分。一个 Topic 可以有多个 Partition ,并且同一 Topic 下的 Partition 可以分布在不同的 Broker 上,这也就表明一个 Topic 可以横跨多个 Broker

3.Kafka 的多副本机制了解吗?

Kafka 为分区(Partition)引入了多副本(Replica)机制。

分区Partition 中的多个副本之间会有一个叫做 leader 的家伙,其他副本称为 follower。我们发送的消息会被发送到 leader 副本,然后 follower 副本才能从 leader 副本中拉取消息进行同步。

生产者和消费者只与 leader 副本交互。你可以理解为其他副本只是 leader 副本的拷贝,它们的存在只是为了保证消息存储的安全性。当 leader 副本发生故障时会从 follower 中选举出一个 leader,但是 follower 中如果有和 leader 同步程度达不到要求的参加不了 leader 的竞选。

4.Kafka 如何保证消息不丢失、不重复、不乱序?

消息不丢失

生产者丢失消息的情况

生产者(Producer)调用 send 方法发送消息之后,消息可能因为网络问题并没有发送过去。

所以,我们不能默认在调用 send 方法发送消息之后消息发送成功了。为了确定消息是发送成功,我们要判断消息发送的结果。但是要注意的是 Kafka 生产者(Producer)使用 send 方法发送消息实际上是异步的操作,我们可以通过 get()方法获取调用结果,但是这样也让它变为了同步操作。

消费者丢失消息的情况

我们知道消息在被追加到 Partition(分区)的时候都会分配一个特定的偏移量offset)。偏移量(offset)表示 Consumer 当前消费到的 Partition(分区)的所在的位置。Kafka 通过偏移量(offset)可以保证消息在分区内的顺序性。

当消费者拉取到了分区的某个消息之后,消费者会自动提交了 offset。自动提交的话会有一个问题,试想一下,当消费者刚拿到这个消息准备进行真正消费的时候,突然挂掉了,消息实际上并没有被消费,但是 offset 却被自动提交了。

解决办法也比较粗暴,我们手动关闭自动提交 offset,每次在真正消费完消息之后再自己手动提交 offset 。但是,细心的朋友一定会发现,这样会带来消息被重新消费的问题。比如你刚刚消费完消息之后,还没提交 offset,结果自己挂掉了,那么这个消息理论上就会被消费两次。

5.Kafka 如何保证消息不重复?

生产端去重

Kafka 从 0.11 版本开始引入了幂等生产者(Idempotent Producer):

开启方式:enable.idempotence=true

原理:

Kafka 为每个 Producer 分配一个 PID(Producer ID)。

每个 Partition 内的消息都有一个递增的 Sequence Number。

Broker 收到消息时,会检查 (PID, Partition, Sequence Number) 是否重复,如果重复则丢弃。

这样,即使生产者因为网络问题重试发送,也不会产生重复消息。

注意:幂等性只能保证单个生产者实例在单个会话内的去重,不能跨会话或跨多个生产者实例。

事务性生产(跨分区、跨会话去重)

开启方式:

props.put("transactional.id", "my-tx-id");

producer.initTransactions();

原理:

transactional.id 用于标识一个事务性生产者,可以跨会话保持唯一性。

Kafka 会将多个分区的写入操作放在一个事务中,要么全部成功,要么全部失败。

配合消费端的事务性消费,可以实现 Exactly Once Semantics(EOS)。

消费端去重

即使生产端保证了幂等性,消费端仍可能因为 offset 提交时机导致重复消费,所以需要:

  1. 手动提交 offset(enable.auto.commit=false)
    在业务处理完成后再提交 offset,避免未处理就提交。
  2. 业务层幂等处理
    给每条消息一个唯一业务 ID(如订单号、事件 ID)。
    在消费端处理前先检查该 ID 是否已处理过(可用 Redis、数据库唯一约束等)。
    如果已处理过则跳过。

6.Kafka 如何保证消息不乱序?

消息不乱序

Kafka 的顺序保证是 分区级别 的,Kafka 只能保证同一个 Partition 内的消息是有序的。不同 Partition 之间的消息是无法保证顺序的,因为它们是并行处理的。

所以,如果想保证消息的顺序,就必须让需要保持顺序的消息全部进入同一个 Partition。

方法一:1 个 Topic 只对应 1 个 Partition

如果一个 Topic 只有一个 Partition,那么这个 Topic 的所有消息都在同一个 Partition 里,自然就有序了。

优点:实现简单,顺序绝对可靠。

缺点:吞吐量受限,因为 Kafka 的并行度取决于 Partition 数量。一个 Partition 只能被一个 Consumer 线程消费,无法利用多线程/多消费者并行处理。

方法二:发送消息时指定 key 或 Partition

Kafka 发送消息时可以指定:

topic:主题

partition:分区编号(可选)

key:键(可选)

value:消息内容

规则:

如果你直接指定 partition,Kafka 就会把消息发到这个固定的 Partition。

如果你指定 key(但不指定 partition),Kafka 会用 key 做哈希计算,得到一个固定的 Partition 编号,这样相同 key 的消息总是进入同一个 Partition。

应用场景:

比如你要保证同一个用户的操作日志有序,可以用 userId 作为 key,这样同一个用户的消息一定进入同一个 Partition,从而保证顺序。

7.Zookeeper 在 Kafka 中的作用

可重复读隔离级别主要是通过 MVCC(多版本并发控制)机制来实现对普通 SELECT 查询的隔离性。在 MVCC 中,每一条记录都可能存在多个版本,每个版本都与创建它的事务 ID 相关联。当一个事务启动时,会创建一个 Read View(读视图),这个 Read View 会记录当前所有活跃的事务 ID。

当事务执行 SELECT 查询时,会根据 Read View 中的信息以及记录的版本号来判断哪些版本的数据是可见的。简单来说,事务只能看到在它启动之前已经提交的事务对数据的修改,以及当前事务自身所做的修改。在可重复读隔离级别下,Read View 通常在事务中的第一个 SELECT 语句执行时创建,并且在整个事务执行期间都会复用这个 Read View,这就保证了在同一个事务中多次读取同一份数据时,得到的结果是一致的,从而避免了不可重复读的问题。

对于执行 UPDATE、DELETE、SELECT ... FOR UPDATE 等当前读的操作,MySQL 会使用行级锁来保证隔离性。在可重复读隔离级别下,InnoDB 会使用Next-key locks来避免幻读。

8.Kafka 高效文件存储设计

这两种隔离级别在使用MVCC实现时的主要区别就是创建read viev的时机不同。

在读已提交这种级别,每次执行select查询语句时都会创建一个read view,所以我们每次查询的结果有可能是不同的,因为这中间有可能有其他事务对这部分数据做了修改。

在可重复读这种级别,只会在第一次执行select语句时创建一个read view,在该事务的后续查询中,每次都使用的是这唯一一个read view,所以每次查询的结果都是相通的。

9.Partition 数据如何保存顺序

在事务提交时,对数据页的修改是先写入到buffer pool内存缓冲区中,再写入到磁盘中去的,不是直接写入到磁盘中去(考虑到内存写入速度更快)。所以说,如果没有redo log,如果在刷盘的过程中出现了数据库服务器故障,那么就无法实现持久性。

所以引入了redo log,在执行事务中的操作时,把操作同步写入到redo log的缓冲区及buffer pool,事务提交时,先写入redo log,再刷盘,这样如果说我们在刷盘的过程中遇到了服务器故障,后面也可以通过redo log里面的内容进行重写,这样就实现了持久化。

10.Kafka 消费模式

好的,分库分表是一种数据库性能优化的重要手段,它的核心思想是将原本存储在单个数据库的单个表中的数据,按照一定的规则拆分到多个数据库或者多个表中,从而提高系统的可扩展性和性能。

什么时候需要分表呢? 通常在以下两种情况下会考虑进行分表:

单表数据量过大(水平分表): 当单张表中的数据量持续增长,达到千万甚至亿级别时,可能会导致查询性能下降、索引失效、事务执行缓慢等问题。这时,我们可以考虑水平分表,也就是将同一张表的数据按照某种规则(例如用户 ID 的范围、时间等)分散到多个结构相同的表中。

单表字段过多(垂直分表): 当一张表中的字段非常多,并且某些字段的使用频率很低或者属于大字段时,可能会影响查询效率。这时,我们可以考虑垂直分表,也就是将一张表的不同列拆分到多个表中,将常用的字段放在一个表中,不常用的或者大字段放在另一个表中。

什么时候需要分库呢? 通常在以下两种情况下会考虑进行分库:

单数据库服务器压力过大(水平分库): 当单个数据库服务器的 CPU、内存、I/O 等资源达到瓶颈,无法支撑当前的并发访问量时,可以考虑水平分库,也就是将相同的表结构复制到多个数据库实例中,然后将不同的数据按照某种规则分散到这些数据库中,从而将请求分散到多台服务器上,提高整体的并发处理能力。

按照业务模块划分(垂直分库): 当数据库中的表比较多,并且可以按照业务模块进行清晰地划分时,可以考虑垂直分库,也就是将不同的业务模块的数据存储在不同的数据库实例中,这样可以提高数据库的组织性和可维护性,并且可以根据业务特点选择更合适的数据库配置。

11. Kafka 系统工具