什么是高水位?
高水位表示分区下副本消息到哪里是算正常提交的。比如如下图:leader副本写到8了,follower副本也写到8,那么这个8就代表要求的副本都写入了。消息到8这里才算提交成功,后面的15写入了也不算提交成功,消费端只能看到8以前的数据。只有要求的副本都写入15后,高水位才会变为15。所以也可以说高水位表示消息的可见性。
后面的LEO表示当前副本消息的最后日志位置,高水位(HW)和LEO结合才能完成副本的同步。
具体怎么同步的?
leader副本上存有当前副本的高水位和LEO和其他副本的LEO。我们用发送一条消息举例。
leader副本 | follower副本 |
HW=2 | HW=2 |
LEO=2 | LEO=2 |
其他副本的LEO=2 |
1,当leader副本写入一条日志后,HW=2,LEO=3,其他副本LEO=2,这个时候follower会按自己的LEO=2来拉取数据。那么他可以拉取到LEO的数据。如下图的这么一个状态:
leader副本 | follower副本 |
HW=2 | HW=2 |
LEO=3 | LEO=3 |
其他副本的LEO=2 |
2,这时follower又来拉取数据了,按当前LEO=3来拉取数据。leader则知道他的LEO=3了,则更新其他副本的LEO=3。leader判断所有副本的LEO都是3时,则更新HW=3。
leader副本<1,1> | follower副本<1,1> |
HW=3 | HW=2 |
LEO=3 | LEO=3 |
其他副本的LEO=3 |
3,leader然后将HW值下发给其他副本,其他副本更新自己HW=1。
leader副本 | follower副本 |
HW=3 | HW=3 |
LEO=3 | LEO=3 |
其他副本的LEO=3 |
通过上面的这3个步骤来实现消息的同步。但是第3步可能会发送失败,这就会造成leader的HW=3了,但是其他副本还是HW=2。这个时候leader副本挂了,其他副本会选举新的leader。那么HW又变为2了,等老副本重启后也会变成follower去请求数据,也会将自己HW从3变更为2。这就造成了消息丢失。为了解决该问题,引入了leader epoch机制来判断日志是否需要截取。我更愿意叫他领导者版本。
领导者版本:
<递增的版本号,该版本号下第一条写入的数据位置>。kafka会缓存该值。一个领导者写入的第一条数据,上面例子中该值就应该等于<1,1>。
还是用上面举例子。如果leader副本挂了,follower副本被推举为新leader。那么他会看当前版本号对应的位置为<1,1>,自己HW=2,LEO=3,1<=3,3需要保留。更新HW=3。只有当版本号对应位置大于自己时才需要截断自己的位置。比如<1,4>那么自己LEO=3,明显自己落后了,需要将数据截断。