1. 分区策略
- 分区的原因
(1)方便在集群中扩展,每个 Partition 可以通过调整以适应它所在的机器,而一个 topic
又可以有多个 Partition 组成,因此整个集群就可以适应任意大小的数据了;
(2)可以提高并发,因为可以以 Partition 为单位读写了。 - 分区的原则
我们需要将 producer 发送的数据封装成一个ProducerRecord
对象。
2.数据的可靠性
为保证 producer 发送的数据,能可靠的发送到指定的 topic,topic 的每个 partition 收到 producer 发送的数据后,都需要向 producer 发送 ack(acknowledgement 确认收到),如果 producer 收到 ack,就会进行下一轮的发送,否则重新发送数据。
副本数据同步机制
方案 | 优点 | 缺点 |
---|---|---|
半数以上完成同步 | 延迟第 | 选举新的leader时,容忍n台节点故障,需要2n+1个副本 |
全部完成同步才发送 | 选举新的leader时,容忍n台节点故障,需要n+1个副本 | 延迟高 |
Kafka 选择了第二种方案,原因如下:
- 同样为了容忍 n 台节点的故障,第一种方案需要 2n+1 个副本,而第二种方案只需要 n+1 个副本,而 Kafka 的每个分区都有大量的数据,第一种方案会造成大量数据的冗余。
- 虽然第二种方案的网络延迟会比较高,但网络延迟对 Kafka 的影响较小。
-
概念:
- AR(Assigned Repllicas)一个partition的所有副本(就是replica,不区分leader或follower)
- ISR(In-Sync Replicas)能够和 leader 保持同步的 follower + leader本身 组成的集合。
- OSR(Out-Sync Relipcas)不能和 leader 保持同步的 follower 集合
- 公式:AR = ISR + OSR
Kafka对外依然可以声称是完全同步,但是承诺是对AR中的所有replica完全同步了吗?
并没有。Kafka只保证对ISR集合中的所有副本保证完全同步。
至于,ISR到底有多少个follower,那不知道,别问,问就是完全同步,你再问就多了。
这就好比网购买一送一,结果邮来了一大一小两个产品。
你可能觉得有问题,其实是没问题的,商家说送的那个是一模一样的了吗?并没有。
ISR就是这个道理,Kafka是一定会保证leader接收到的消息完全同步给ISR中的所有副本。
而最坏的情况下,ISR中只剩leader自己。
因为ISR的机制就保证了,处于ISR内部的follower都是可以和leader进行同步的,一旦出现故障或延迟,就会被踢出ISR。
ISR 的核心就是:动态调整
K a f k a 采 用 的 就 是 一 种 完 全 同 步 的 方 案 , 而 I S R 是 基 于 完 全 同 步 的 一 种 优 化 机 制 。 Kafka采用的就是一种完全同步的方案,而ISR是基于完全同步的一种优化机制。 Kafka采用的就是一种完全同步的方案,而ISR是基于完全同步的一种优化机制。
-
1、
Leader选举(选取ISR表中follower 速度慢)
-
ISR(in-sync replicas)列表。每个分区的 leader 会维护一个 ISR 列表,ISR 列表里面就是 follower 副本的 Borker 编号,
只有“跟得上” Leader 的 follower 副本才能加入到 ISR 里面
,这个是通过 replica.lag.time.max.ms 参数配置的。只有 ISR 里的成员才有被选为 leader 的可能。 -
当 Leader 挂掉了,而且 unclean.leader.election.enable=false 的情况下,Kafka 会从 ISR 列表中选择第一个 follower 作为新的 Leader,因为这个分区拥有最新的已经 committed 的消息。通过这个可以保证已经 committed 的消息的数据可靠性。
-
因此,为了保证数据的可靠性,需要配置一下几个参数:
producer 级别:acks=all(或者 request.required.acks=-1),同时发生模式为同步producer.type=sync topic 级别:设置 replication.factor>=3,并且min.insync.replicas>=2;
broker 级别:关闭不完全的 Leader 选举,即 unclean.leader.election.enable=false;即 ack机制设置为所有follower均收到消息才响应;(producer)发生模式设置为同步; 副本数设置大于等于3,最小同步数设置为2;关闭不完全的Leader选举。
-
总结
- leader会维持一个与其保持同步的replica集合,该集合就是ISR,每一个leader partition都有一个ISR,leader动态维护, 要保证kafka不丢失message,就要保证ISR这组集合存活(至少有一个存活),并且消息commit成功,Partition leader 保持同步的 Partition Follower 集合, 当 ISR 中的Partition Follower ,完成数据的同步之后,就会给 leader 发送 ack 如果Partition follower长时间(replica.lag.time.max.ms) 未向leader同步数据,则该Partition Follower将被踢出ISR,Partition Leader 发生故障之后,就会从 ISR 中选举新的 Partition Leader。当replica重新追上了leader,OSR中的replica就会重新加入ISR中。 replica.lag.time.max.ms的误区
- 【只要在 replica.lag.time.max.ms 时间内 follower 有同步消息,即认为该 follower 处于 ISR 中】
-
是不是可以理解为,follower有个定时任务,只要在replica.lag.time.max.ms时间内去leader那pull数据就行了。
其实不是的。千万不要这么认为,因为这里还涉及一个 速率问题(你理解为蓄水池一个放水一个注水的问题)。
如果leader副本的消息流入速度大于follower副本的拉取速度时,你follower就是实时同步有什么用?
典型的出工不出力,消息只会越差越多,这种follower肯定是要被踢出ISR的。
当follower副本将leader副本的LEO之前的日志全部同步时,则认为该follower副本已经追赶上leader副本。
此时更新该副本的lastCaughtUpTimeMs标识。
Kafka的副本管理器(ReplicaManager)启动时会启动一个副本过期检测的定时任务,
会定时检查
当前时间与副本的lastCaughtUpTimeMs差值是否大于参数
replica.lag.time.max.ms指定的值。
所以replica.lag.time.max.ms
的正确理解是:
follower在过去的replica.lag.time.max.ms时间内,已经追赶上leader一次了。
-
follower到底出了什么问题?
-
两个方面,一个是Kafka自身的问题,另一个是外部原因
-
Kafka源码注释中说明了一般有两种情况会导致副本失效:
-
(1) follower副本进程卡住,在一段时间内根本没有想leader副本发起同步请求,比如频繁的Full GC。
(2) follower副本进程同步过慢,在一段时间内都无法追赶上leader副本,比如IO开销过大。 -
(1) 通过工具增加了副本因子,那么新增加的副本在赶上leader副本之前也都是处于失效状态的。
(2) 如果一个follower副本由于某些原因(比如宕机)而下线,之后又上线,在追赶上leader副本之前也是出于失效状态。
2、Producer 往 Broker 发送消息
-
ack应答机制:Kafka 在 Producer 里面提供了消息确认机制。也就是说我们可以通过配置来决定有几个副本收到这条消息才算消息发送成功。
-
对于某些不太重要的数据,对数据的可靠性要求不是很高,能够容忍数据的少量丢失,
所以没必要等 ISR 中的 follower 全部接收成功。 -
Kafka 为用户提供了
三种可靠性级别
,用户根据对可靠性和延迟的要求进行权衡, 选择以下的配置。(在 0.8.2.X 版本之前是通过request.required.acks 参数设置的。
ack参数配置:
acks=0:producer不会等待任何来自服务器(broker)的响应(ack)。 这一操作提供了一个最低的延迟,broker一接收到还没有写入磁盘就已经返回,当broker故障时有可能丢数据。
acks=1(默认值):producer 等待brokerd的ack,partition的leader落盘成功后返回ack,如果在follower同步成功之前leader故障,那么将会丢失数据。
acks=-1(all):producer 等待brokerd的ack,partition的leader和follower落盘成功后返回ack,但是如果在follower同步完成后,broker发送ack之前,leader发生故障,那么会造成数据重复。
Producer 发送消息还可以选择同步(默认,通过 producer.type=sync 配置) 或者异步(producer.type=async)模式。如果设置成异步,虽然会极大的提高消息发送的性能,但是这样会增加丢失数据的风险。如果需要确保消息的可靠性,必须将 producer.type 设置为 sync。
-
3、Topic 分区副本 (数据的冗余备份)
-
在 Kafka 0.8.0 之前,Kafka 是没有副本的概念的,那时候人们只会用 Kafka 存储一些不重要的数据,因为没有副本,数据很可能会丢失。为了保证数据的可靠性,Kafka 从 0.8.0 版本开始引入了分区副本。
-
每个分区可以人为的配置几个副本(比如创建主题的时候指定 replication-factor,也可以在 Broker 级别进行配置 default.replication.factor),一般会设置为3。
-
Kafka 可以保证单个分区里的事件是
有序的
,分区可以在线(可用),也可以离线(不可用)。在众多的分区副本里面有一个副本是 Leader,其余的副本是 follower,所有的读写操作都是经过 Leader 进行的
,同时 follower 会定期地去 leader 上复制数据。当 Leader 挂掉之后,其中一个 follower 会重新成为新的 Leader。通过分区副本,引入了数据冗余,同时也提供了 Kafka 的数据可靠性。