这里不得不提 2 个重要参数:
# kafka 0.9.0 版本之前存在的参数
# 这个参数规定了 follower 如果落后 Leader 的消息数量超过了这个参数指定的数量之后,
# 就会认为 follower 是 out-of-sync,就会从 ISR 列表里移除
replica.lag.max.messages
# kafka 0.10.x 新增的参数
# 与 leader 上一次交互时间超过阈值就会把 follower 剔除出 ISR
replica.lag.time.max.ms
为什么新版本移除
replica.lag.max.messages参数?
- 设置太大了,影响真正“落后”
follower的移除- 设置的太小了,导致
follower的频繁进出。无法给定一个合适的replica.lag.max.messages的值,故此,新版本的Kafka移除了这个参数。
一些概念:
AR(Assigned Repllicas)一个partition的所有副本(就是replica,不区分leader或follower)ISR(In-Sync Replicas) 能够和Leader保持同步的follower + leader本身 组成的集合。OSR(Out-Sync Relipcas)不能和Leader保持同步的follower集合
这里就不得不提 Producer 的一个重要的参数:acks
acks=0:不需要等待服务器的确认. 这是retries设置无效. 响应里来自服务端的offset总是-1,producer只管发不管发送成功与否。延迟低,容易丢失数据。acks=1:表示leader写入成功(但是并没有刷新到磁盘)后即向producer响应。延迟中等,一旦leader副本挂了,就会丢失数据。acks=all:等待数据完成副本的复制, 等同于-1. 假如需要保证消息不丢失, 需要使用该设置. 同时需要设置unclean.leader.election.enable为true, 保证当ISR列表为空时, 选择其他存活的副本作为新的leader.
拿 replica.lag.max.messages参数 举个栗子:
前提:
# 副本有 3 个,1 个 leader 和 2 个 follower
replication-factor = 3
# 最小同步数为1,1个副本写入数据就认为写入成功
min.insync.replicas = 2
# 这个参数规定了 follower 如果落后 Leader 的消息数量超过了这个参数指定的数量之后,
# 就会认为 follower 是 out-of-sync,就会从 ISR 列表里移除
replica.lag.max.messages = 3
- 初始时:
Leader和Follower均有 3 条数据,LEO在Offset = 3的位置
2. 进来一条数据:
Leader 和 Follower 副本B 均成功写入新数据,Follower副本C 没有写入(因为所在机器性能突然降低),此时 Leader 的 LEO = 4,HW = 3
> 如果这时候,要求所有副本写入成功才算写入成功`min.insync.replicas = 3` ,那么生产者会认为还没写入成功。
Follower副本C 突然fullgc:fullgc一般持续好几秒
-
进来三条数据:
Leader和Follower副本B 均成功写入新数据,Leader的LEO = 7, HW = 3注意:消费者只能读取小于
HW的数据,大于HW的数据是读取不了的。
-
触发阈值:
Follower副本C的LEO = 3跟Leader的LEO = 7差距超过 3 了,这时就会把Follower副本C 移除了。这时候,只有2个副本在,
HW也往前推进了,此时消费者可以消费全部数据了。
Follower副本Cfullgc结束:这时,副本C 拼命同步数据,慢慢地LEO又控制在replica.lag.max.messages限定的范围内,此时副本C 就会重新加回到ISR列表里
综上,简单走了一遍 ISR 维护过程。
现在想下,导致 Follower 更新跟不上的情况主要有三种:
Follower所在机器的性能比那差: 比如网络负载过高、IO负载过高、CPU负载过高、机器负载过高等,都有可能导致机器性能变差。Follower所在的Broker进程卡顿: 常见就是fullgc问题- 动态调节副本数量: 动态增加了
partition的副本,就会增加新的Follower,此时新的Follower会拼命从Leader上同步数据,但是这个需要一段时间