一,存在的一致性问题
1,生产者和Kafka存储一致性的问题。即生产了多少条消息,就要成功保存多少条消息,不能丢失,不能重复,更重要的是不丢失。其实就是要确保消息写入成功,这可以通过acks=-1来保证,保证所有ISR的副本都是一致的,即一条消息写入所有副本才算写入成功。这里有个问题在于,ISR中的副本数不一定等于集群副本数,如果ISR为1,可能导致数据丢失,在这种情况下,相当于Leader写入成功即认为消息写入成功,但在极端情况下,Leader写入成功后宕机,其他副本未同步,生产者又认为写入成功,不会尝试再次写入,则这条消息丢失。这种情况可以通过设置ISR队列至少为2来解决,即保证一条消息至少写入两个副本才算成功。使用acks=-1假设ISR=2能保证消息不丢失,不能保证消息不重复,要保证消息不重复,则要开启幂等或者事务特性。开启幂等,同一个分区的消息会加上序列号,已经提交的序列号再次提交时会被认为是重复的,不会被再次保存。不过幂等限于同一个会话、同一个分区。
幂等是通过生产者从broker获取pid,并对每个partition维护一个随消息自增的seqNumber,形成一个三元组<pid,partition,seqNumber>主键,重复的主键被认为是重复数据,会被丢弃。
因为PID会在生产者启动时申请,所以不同会话的PID不一致,同时考虑到生产者的数据来源,不同分区的可能PID也不一致,所以无法在topic维度统一seqNumber,只能在partition维度统一管理seqNumber
2,副本的一致性
消息先写入leader,follower从leader复制,必然会出现follower和leader不一致的现象,如果我们设置ISR至少等于2,可以减小这种风险,ISR至少等于2表示消息至少写入两个副本,这样就保证类至少两个副本是完全同步的,而leader只会从ISR中选举,则能保证当leader失活之后,新的leader也能不丢失数据。
3,消费一致性
leader稳定运行,通过消费者提交的offset控制消费进度,保证消息不丢失,不能保证不重复。
当生产者设置ack=-1时,很多一致性问题都会被解决,acks=-1要求副本同步复制,所有副本复制完成则认为消息保存成功,不会出现副本不一致的问题。leader失活重新选举的新leader和原来的leader也会保持数据的一致性。
但同步复制会导致kafka的性能降低。当acks不设置为-1时,如何尽量保证数据的一致性,从而取得性能和可靠性的平衡呢?
Kafka提供了很多机制来平衡性能和可靠性:
1,ISR,ISR保存了最低限度同步的副本集合,leader只能从ISR中获取,ISR的标准是follow和leader上次同步成功时间距现在不能超过10秒,超过10S会将副本从ISR中剔除,这样在副本间异步复制的情况下,尽量保证follower和leader的同步。
2,LEO,log end offset,每个副本最后一条消息的offset+1就是offset,每个副本单独维护,ISR中最小的LEO称之为HW,小于HW的offset才能被消费者消费。HW能避免消费者重复消费,保证消费者消费的一致性。HW也是副本重新加入ISR的标准。
如果没有HW,消费者直接将leader的所有消息消费,offset为leader的LEO,在ISR其他副本落后于leader的情况下,leader失活,重新选举其他副本作为leader,生产者写入新的数据到leader,但由于新leader的offset落后于旧leader,而记录的消费者消费的offset是旧leader的LEO,导致新写入的offset小于旧Leader LEO的数据不会再被消费,出现消息丢失的情况。
当如果使用HW,重选leader后新写入的消息不会丢失,不过旧leader上高于HW的消息则可能丢失。
3,被踢出ISR的副本的HW赶上Leader的HW后,会被重新纳入ISR管理。
Kafka正是通过异步复制、ISR、HW来平衡消息系统的效能和可靠性的。