kafka问题及思考
kafka问题及思考
[TOC]
解释下 Unclean 领导者选举?有什么利弊?
非同步副本落后 Leader 太多,如果选择这些副本作为新 Leader,就可能出现数据的丢失, 选举这种副本的过程称为 Unclean 领导者选举。Broker 端参数 unclean.leader.election.enable
控制是否允许 Unclean 领导者选举。
开启 Unclean 领导者选举可能会造成数据丢失,但好处是,它使得分区 Leader 副本一直存在,不至于停止对外提供服务,因此提升了高可用性。反之,禁止 Unclean 领导者选举的好处在于维护了数据的一致性,避免了消息丢失,但牺牲了高可用性。
相当于在 CAP 中,牺牲一定的一致性,提高了 AP。
但是,还是通常不建议开启 Unclean 领导者选举,除非真的是数据不重要,不在乎一致性,不然我们还是来提高可用性
如果节点数据不完整,大部分情况下是不该也不能够被选举为领导者,在 Raft 和 ZAB 中都有类似的限制,日志完整性过低的节点是不能当选为 Leader 的。
哪些场景会出现数据丢失?
Producder 投递消息阶段
- 异步批量发送:主流的 kafka 客户端都是异步批量发送的,即应用代码调用 producer.send(msg) 发送消息会立刻返回,但是底层库会积累一批消息才会投递出去,通常可以根据 消息数量和等待时间两个个参数进行配置,只要触发一个就会投递出去。这种异步批量处理,可以大大提高吞吐能力,在很多场景都有类似的设计。
-
如果在消息只是写入应用服务的本地缓冲区应用节点就挂了,还没来得及投递到 Broker 节点,消息自然也就丢失了。
- 当然也支持配置成同步发送,但是这回大大降低性能。
- 有的库也支持通过 callback(回调)来判断消息是否成功投递到 Broker。
Broker 存储消息阶段
- 非同步副本落后 Leader 太多,如果选择这些副本作为新 Leader,就可能出现数据的丢失, 选举这种副本的过程称为 Unclean 领导者选举。
- 假设保存副本的所有节点都彻底损毁,自然数据也就丢失了。
Consumer 消费消息阶段
- 如果消费端,没能遵循先成功消费消息,再提交消费位移的顺序,就可能造成消息的丢失。
如何排查,确认 kafka 消息是否存在丢失现象?
如何实现消息不丢失?
在很早的版本 kafka 会丢失消息。后来的版本,只要合理使用 kafka,其实还是可以实现消息不丢失,当然这也是在一定条件下的不丢失,如果只是单机房部署,机器都烧没了,自然不可能不丢失数据。
总体上来说, kafka 保证,已提交 并且已持久化的消息不丢失。
已提交:这个可配置,成功保存到了多少个 Broker 节点算是提交成功。假设成功保存到了 N 个节点上,只要这 N 个副本中,有存活的消息就不会丢失。
Producer
-
Producer 使用至少一次投递;
-
的库如果是异步批量投递消息的,需要能通过回调来判断消息是否成功投递,
Broker
-
使用副本数大于等于 3个。副本越多自然越安全,但是也意味着开销也越多;
- 确保成功提交到多副本才算成功提交,如果必要的话,可以考虑设置
acks = all
,即所有的 Broker 都提交成功了才算成功保存; - 关闭 unclean 领导选举,避免落后 Leader 太多的副本当选为 Leader;
Consumer
- 不开启自动提交位移,手动提交消费位移,遵循消费成功再提交消费位移的顺序;
数据“已提交”是什么意思?是提交到磁盘还是 buffer 缓冲区?这对消息的可靠性有影响?
消息顺序问题
Kafka 只能保证同一个分区中的消息是有序的。
如果第一批消息写入失败,第二批消息写入成功了,需要重新发送第一批消息,这样会造成消息乱序。如果要避免这中问题,在需要保证消息顺序的场合建议把参数 max.in.flight.requests.per.connection
配置为1。当然这样也会造成性能的下降。会造成类似 TCP 的 head line of block 的问题。
至多一次,精确一次,最少一次投递模型都在 kafka 实现了吗?
- 最多一次(at most once):消息可能会丢失,但绝不会被重复发送。
- 至少一次(at least once):消息不会丢失,但有可能被重复发送。
- 精确一次(exactly once):消息不会丢失,也不会被重复发送。
Kafka 默认提供的交付可靠性保障是第二种,即至少一次。
如果要实现精确一次功能,只能通过如幂等设计,事务机制保证。 kafka 的幂等只支持单分区和单会话的幂等。kafka 事务能实现夸分区和夸会话的幂等,但是性能也更差。
消息重复,重复消费问题?
考虑到网络的复杂性和不稳定性,很多场景都需要消息可靠投递,所以多是至少一次的投递。
在消费端,会通过消费位移来控制消费,如果通常是先消费成功,再提交位移,如果刚消费了,但是没能成功提交位移就挂了,那么下次消费自然也就会重复消费。
所以,如果真需要避免重复消息,最好额外做幂等设计。幂等设计本质也是通过一个唯一的 token 来区分。常见的幂等设计,如 uuid,snowflake, 数据库唯一约束,流水号,redis 唯一key, 状态机,等等。
如果需要幂等,在kafka中可以把 enable.idempotence
被设置成 true 后,Producer 自动升级成幂等性 Producer,其他所有的代码逻辑都不需要改变。需要注意的是
- 这是保证单分区上的幂等性,即一个幂等性 Producer 能够保证某个主题的一个分区上不出现重复消息,它无法实现多个分区的幂等性。
- 其次,它只能实现单会话上的幂等性,不能实现跨会话的幂等性。
所以如果需要全局的幂等化,还是要通过其他机制来保证。如 kafka 事务能够保证跨分区、跨会话间的幂等性,但是性能也更差。
消费群组带来了什么好处?
kafka 的分区和消费群组的设计,是其提高并发能力关键因素之一。也是比较巧妙的设计。
通过消费群组把消费者划分成彼此隔离互补影响的分组。每一个分区只能被一个消费组中的一个消费者所消费。如果需要增加消费能力,只需要增加同一消费群组消费者的数量(不能超过分区数),就可以扩展消费能力。
此外,由于分区和消费群组的限制,但消费消费一个分区避免了消费者之间争抢消息的开销。
为什么说一个消费群组的消费者不能多于分区数?
每一个分区只能被一个消费组中的一个消费者所消费。但可以被不同的消费组同时消费,但是同一个消费者可以同时消费多个分区。
同一消费群组的消费者不会多于分区数。通过这个限制,同一个消费组中的消费者,可以不重复的消费同一个topic中的消费者。
消费 offset 提交问题?
在旧消费者客户端中,消费位移是存储在 ZooKeeper 中的。
新消费者客户端中,消费位移存储在 Kafka 内部的主题__consumer_offsets
中。
消费位移,可以通过参数 enable.auto.commit
配置,默认值为 true, 即自动提交的。自动位移提交正常情况下不会发生消息丢失或重复消费的现象,但当应用崩溃时,还是可能的出现问题的。
在一些场景,如写入数据库、写入本地缓存后才提交位移的,或者是更加复杂的业务处理。还是建议手动提交消费位移。
kafka 消费位移保存位置为什么过去的 zookeeper 改为 Broker 的主题中?
zookeeper 通常作为分布式协调服务,它实现了强一致性。自身是分布式部署,在实现强一致上,使用二阶段提交的 ZAB 协议,而且写请求都是在 Leader 节点进行的,其性能不高,而且性能很难按需扩容,并不适合高频的交互场景。而消费位移的提交的一个非常高频的场景,这显然与 kafka 高性能高吞吐的定位不符。
kafka 的消费模型是 push 还是 pull ?两者有什么不同?
pull 模型,客户端可以根据自己的消费能力,去拉取消息;但是也总不能一直while循环拉取吧。如果需要sleep,就存在实时性不足的问题了。
push 模型,服务端主动push 消息给消费者,这样就保证了实时性,但是如果push的过多,可能超过了客户端的承载能力。甚至消息丢失。
在 kafka 中是采用 pull 模型。Kafka 中的消息消费是一个不断轮询的过程,重复地调用 poll(),而 poll() 方法返回的是所订阅的主题(分区)上的一组消息。
在 rabbitmq 中是采用 push 模型,但是为了防止 push 过多,超出了客户端的消费能力,rabbitmq 如果发现有过多的消息没有被 ACK 时会暂停push 消息。
消息堆积问题
如果一个 Broker 节点有非常多的 topic 同时写,会有什么问题?
如果一次 Broker 上有非常多的 topic 同时进行写消息操作,其实就是丧失了 kafka 顺序写磁盘的能力,而更像是随机 IO了,这个会大大降低 kafka 吞吐能力。
为此,大方向是适当减少统一 Broker 上的 topic 或者说分区数量。如按业务隔离,适当的合并 topic 。
目前 kafka 只有 Leader 副本对外服务? Follower 副本只起到冗余备份的作用,为什么是这样设计?社区也有开启 Follower 提供服务的声音, 如果开启了需要注意什么?
首先 Follower 副本起到同步 Leader 副本的冗余备份的作用,当Leader 挂了,可以选举出新的 Leader 这样可以提高可用性和数据的安全性。
读写都在 Leader 不是一定程度降低了性能吗?没错,但是 kafka 可以通过多分区,多消费群组的方式,批量化,顺序写日志等一系列手段大大提吞吐和性能。尤其是多分区机制,已经起到了 sharding 的作用。
如果 Follower 对外服务,就是首先就是引入 Leder, Follower 之间数据一致性的问题。这样相比之前维护一致性的成本更高。在很多强调一致性的场景都是已只以 Leader 为准,或是写都在 Leader 上。如 Raft,ZAB 协议, chubby 等。
kafka 的分区策略?
kafka 的分区机制,其实就是一种数据分片技术的案例,它提供了负载均衡的能力,同时通过分区实现了并行消费的能力。
很多分布式的系统都会涉及到。如 MongoDB,Elasticsearch,HBase,Cassandra, Redis Cluster 等系统都有类似的概念,虽然叫法各异,但是本质上都是分片或者说分区的应用。
常见的分片策略:随机,轮询,加权轮询,取模,哈希,一致性哈希,有界负载一致性哈希等等。
kafka 提供了一些常见的分区策略,当然也支持自定义分区策略。
- Round-robin 轮询策略,即顺序分配。这也是默认的策略,好处就是实现简单,而且非常的均衡。
- Randomness 随机策略,不建议采用。
- 按消息键保序策略。可以为每个消息指定一个 key,可以用与业务相关含义的值。
- 其他的,如按地理位置策略等等。
kafka 是如何判定跟随者副本与领导者是否同步的?
In-sync Replicas(ISR),ISR 副本集合。
在ISR 中的副本都是与 Leader 同步的副本,相反,不在 ISR 中的追随者副本就被认为是与 Leader 不同步的。
能够进入到 ISR 的追随者副的判断条件:
- Kafka 判断 Follower 是否与 Leader 同步的标准,不是看相差的消息数;
- 而是要参考参数
replica.lag.time.max.ms
,含义是 Follower 副本能够落后 Leader 副本的最长时间间隔,当前默认值是 10 秒。只要一个 Follower 副本落后 Leader 副本的时间不连续超过 10 秒,那么 Kafka 就认为该 Follower 副本与 Leader 是同步的,即使此时 Follower 副本中保存的消息明显少于 Leader 副本中的消息。
如果副追上了 Leader 的进度就能够重新被加回 ISR 的。ISR 是一个动态调整的集合,而非静态不变的。
压缩可以启动用CPU资源换空间的作用,列举一些压缩算法,效果如何?
如 GZIP、Snappy 和 LZ4、zstd
看一个压缩算法的优劣,有两个重要的指标:
- 压缩比, 压缩比越高越好;
- 压缩 / 解压缩吞吐量, 如每秒能压缩或解压缩多少 MB 的数据, 也是越高越好;
吞吐量方面:LZ4 > Snappy > zstd 和 GZIP
压缩比方面,zstd > LZ4 > GZIP > Snappy
如果 Producer 程序运行机器上的 CPU 资源要很充,需要减少带宽的可以考虑开启压缩。对于减少带宽占用方面, zstd 效果非常明显。
kafka 日志存储
通过 log.dirs 参数,可以指定 kafka 数据的存放目录。
kafka 的日志分段存储,会把 topic 中一个 parition 大文件分成多个小文件段,通过多个小文件段(segment),这样分段存储便于管理,清除减少磁盘占用。 一般一个分区内的 .log 文件不超过 1G 。
通过索引信息可以快速定位 message 和确定 response 的最大大小。
00000000000005367851.index
00000000000005367851.log
00000000000005367851.timeindex
通过哪些手段,可以提高 kafka 的可用性?
kafka的哪些设计起到了提高吞吐能力的作用?
- 分区
- 消费群组
- 消息异步,批量发送
- 顺序追加写日志
- sendFile 零拷贝
kafka 与 rabbitmq 的对比?
rabbitmq 消息放在内存中,会随着内存占用的上升,性能下降明显
消费模型 kafka 是 pull, 而 rabbitmq 是 push 方式。
kafka 支持消费群组和分区的能力,这个对于并行能力的提升很大。
kafka 通过消费位移,消费群组实现多次消费的能力。而 rabbit 消费完了消息会被删除,如果要实现广播的能力需要存储多分数据。
rabbitmq 里有交换器的概念,生产者会先把消息把给交换器
TODO
如果让你设计一个消息队列,需要考虑哪些问题?
IO模型
连接管理,
协议设计
序列化选择
压缩算法
拦截器
消息分区,副本
消息持久化问题,顺序写日志?
消息顺序问题,如何避免消息丢失,可靠性投递,可靠性消费
如何提高吞吐,批量化?异步化? 去锁化? 消费群组,
监控问题
节点健康问题
数据分片,负载均衡问题,数据同步问题
如何弹性扩容,
文件系统问题
CAP,如何在可靠性,一致性,可用性 寻求均衡