Kafka【十一】数据一致性与高水位(HW :High Watermark)机制

【1】数据一致性

Kafka的设计目标是:高吞吐、高并发、高性能。为了做到以上三点,它必须设计成分布式的,多台机器可以同时提供读写,并且需要为数据的存储做冗余备份。

图中的主题有3个分区,每个分区有3个副本,这样数据可以冗余存储,提高了数据的可用性。并且3个副本有两种角色,Leader和Follower,Follower副本会同步Leader副本的数据。

一旦Leader副本挂了,Follower副本可以选举成为新的Leader副本, 这样就提升了分区可用性,但是相对的,在提升了分区可用性的同时,也就牺牲了数据的一致性。

我们来看这样的一个场景:一个分区有3个副本,一个Leader和两个Follower。Leader副本作为数据的读写副本,所以生产者的数据都会发送给leader副本,而两个follower副本会周期性地同步leader副本的数据,但是因为网络,资源等因素的制约,同步数据的过程是有一定延迟的,所以3个副本之间的数据可能是不同的。具体如下图所示:

此时,假设leader副本因为意外原因宕掉了,那么Kafka为了提高分区可用性,此时会选择2个follower副本中的一个作为Leader对外提供数据服务。此时我们就会发现,对于消费者而言,之前leader副本能访问的数据是D,但是重新选择leader副本后,能访问的数据就变成了C,这样消费者就会认为数据丢失了,也就是所谓的数据不一致了。

为了提升数据的一致性,Kafka引入了高水位(HW :High Watermark)机制,Kafka在不同的副本之间维护了一个水位线的机制(其实也是一个偏移量的概念),消费者只能读取到水位线以下的的数据。这就是所谓的木桶理论:木桶中容纳水的高度,只能是水桶中最短的那块木板的高度。这里将整个分区看成一个木桶,其中的数据看成水,而每一个副本就是木桶上的一块木板,那么这个分区(木桶)可以被消费者消费的数据(容纳的水)其实就是数据最少的那个副本的最后数据位置(木板高度)。

也就是说,消费者一开始在消费Leader的时候,虽然Leader副本中已经有a、b、c、d 4条数据,但是由于高水位线的限制,所以也只能消费到a、b这两条数据。

这样即使leader挂掉了,但是对于消费者来讲,消费到的数据其实还是一样的,因为它能看到的数据是一样的,也就是说,消费者不会认为数据不一致。

不过也要注意,因为follower要求和leader的日志数据严格保持一致,所以就需要根据现在Leader的数据偏移量值对其他的副本进行数据截断(truncate)操作。

【2】HW在副本之间的传递

HW高水位线会随着follower的数据同步操作而不断上涨,也就是说,follower同步的数据越多,那么水位线也就越高,那么消费者能访问的数据也就越多。接下来我们就看一看,follower在同步数据时HW的变化。

首先,初始状态下,Leader和Follower都没有数据,所以和偏移量相关的值都是初始值0,而由于Leader需要管理follower,所以也包含着follower的相关偏移量(LEO)数据。

生产者向Leader发送两条数据,Leader收到数据后,会更新自身的偏移量信息。

复制代码
Leader副本偏移量更新:LEO=LEO+2=2

接下来,Follower开始同步Leader的数据,同步数据时,会将自身的LEO值作为参数传递给Leader。此时,Leader会将数据传递给Follower,且同时Leader会根据所有副本的LEO值更新HW。

复制代码
Leader副本偏移量更新:HW = Math.max[HW, min(LeaderLEO,F1-LEO,F2-LEO)]=0

由于两个Follower的数据拉取速率不一致,所以Follower-1抓取了2条数据,而Follower-2抓取了1条数据。Follower再收到数据后,会将数据写入文件,并更新自身的偏移量信息。

复制代码
Follower-1副本偏移量更新:
LEO=LEO+2=2
HW = Math.min[LeaderHW, LEO]=0
Follower-2副本偏移量更新:
LEO=LEO+1=1
HW = Math.min[LeaderHW, LEO]=0

接下来Leader收到了生产者的数据C,那么此时会根据相同的方式更新自身的偏移量信息

复制代码
Leader副本偏移量更新:LEO=LEO+1=3

follower接着向Leader发送Fetch请求,同样会将最新的LEO作为参数传递给Leader。Leader收到请求后,会更新自身的偏移量信息。

复制代码
Leader副本偏移量更新:HW = Math.max[HW, min(LeaderLEO,F1-LEO,F2-LEO)]=1

此时,Leader会将数据发送给Follower,同时也会将HW一起发送。

Follower收到数据后,会将数据写入文件,并更新自身偏移量信息

复制代码
Follower-1副本偏移量更新:
LEO=LEO+1=3
HW = Math.min[LeaderHW, LEO]=1
Follower-2副本偏移量更新:
LEO=LEO+1=2
HW = Math.min[LeaderHW, LEO]=1

因为Follower会不断重复Fetch数据的过程,所以前面的操作会不断地重复。最终,follower副本和Leader副本的数据和偏移量是保持一致的。

上面演示了副本列表ISR中Follower副本和Leader副本之间HW偏移量的变化过程,但特殊情况是例外的。比如当前副本列表ISR中,只剩下了Leader一个副本的场合下,是不需要等待其他副本的,直接推高HW即可。

相关推荐
程序猿阿伟3 小时前
《分布式软总线架构下,设备虚拟化技术的深度剖析与优化策略》
分布式·架构
End9285 小时前
Hadoop的三大结构及其作用?
大数据·hadoop·分布式
461K.9 小时前
spark与hadoop的区别
大数据·运维·hadoop·分布式·spark·intellij-idea
日月星辰Ace10 小时前
基于 AWS DynamoDB 分布式锁
分布式·aws
LUCIAZZZ11 小时前
KRaft面试思路引导
java·spring boot·算法·面试·kafka·操作系统·raft
愚公搬代码11 小时前
【愚公系列】《Python网络爬虫从入门到精通》058-自定义分布式爬取诗词排行榜数据
分布式·爬虫·python
Gvemis⁹11 小时前
Spark-SQL 四(实验)
大数据·分布式·spark
2401_8242568612 小时前
Spark-SQL(四)
大数据·分布式·spark
梦想养猫开书店12 小时前
34、Spark实现读取XLS文件
大数据·分布式·spark
Blossom.11815 小时前
量子计算在金融领域的应用与展望
数据库·人工智能·分布式·金融·架构·量子计算·ai集成