BookKeeper存储设计浅析

BookKeeper存储设计浅析前言本文作为 Pulsar 系列的第二篇文章 主要介绍 ApacheBookKe 在存储上的设计 主要聚焦于以下两点 BookKeeper 的读写流程是怎样的 怎么去存储数据 多副本存储下 BookKeeper 如何处理一致性问题同时强调下 BookKeeper 本身是个独立的项目 本文是在 Pulsar 原理探究过程中对 BookKeeper 存储设计的系统性学习总结 读写流程设计 BookKeeper 采用读写分离的设计读写流程示意图 更细化的读写流程图 写流程设计 1

前言

本文作为Pulsar系列的第二篇文章,主要介绍Apache BookKeeper在存储上的设计,主要聚焦于以下两点:

  • BookKeeper的读写流程是怎样的,怎么去存储数据
  • 多副本存储下BookKeeper如何处理一致性问题

同时强调下BookKeeper本身是个独立的项目,本文是在Pulsar原理探究过程中对BookKeeper存储设计的系统性学习总结。

 

读写流程设计

BookKeeper采用读写分离的设计

读写流程示意图:

 

image.png

更细化的读写流程图:

image.png

写流程设计

1)同步流程

  1. 将数据写入Journal文件中(即预写日志文件),如果同时有多个数据写入,可进行批量提交(Group commit),刷到Journal磁盘中
  2. 将数据写入内存中(即memtable/Write Cache,作为写缓存)
  3. 返回写入成功响应,注意到这里整个流程都是同步的

2)异步流程

  1. 异步将缓存的数据进行聚合+排序,聚合针对同一个Ledger,排序针对一个Ledger下以时间为顺序的数据
  2. 将排序好的Entry放入Log Entry File,刷新(flush)到磁盘中
  3. 以<(LedgerID, EntryID), EntryLogID>写入RocksDB,用于在随机读时可以快速找到Entry

值得一提的是,Log Entry File创建的条件是:bookie刚建立,或者旧的Log Entry File已经达到文件大小阈值。这意味着不同的Ledger会聚合到同一个Log Entry File。

另外Journal File创建的条件是:bookie刚建立,或者旧的Journal File已经达到Journal文件大小阈值。这意味着Journal遇到多个Ledger写入时顺序写不会变成随机写,也就是不会像kafka分区一多就顺序写变随机写。

 

读流程设计

  1. 先从写缓存中读(对于Tailing Reads即尾部读来说,由于读取的是最新的,一般可以从写缓存中拿到数据)
  2. 如果写缓存不命中,从读缓存(Read Cache)读取数据
  3. 如果读缓存不命中,则以查询的LedgerID+EntryID通过RocksDB得到数据对应的物理存储位置EntryLogID,然后从Log Entry Files(磁盘上)读取数据
  4. 把读取到的数据回填到读缓存中
  5. 返回读取成功响应

 

一致性设计

对于分布式存储系统,为了高可用,多副本是其通用的解决方案,但也带来了一致性的问题,包括:

  • 数据怎么算可读,如何避免读到未ack的数据
  • 出现脑裂情况时,如何避免多Writer写入同一个topic/partition导致数据不一致

接下来看BookKeeper是如何解决这些一致性问题的。

 

一致性模型

在介绍其读写一致性之前,先看下 BK 的一致性模型

一致性模型-开始时的状态:

image.png

对于 Write 操作而言,writer 不断添加记录,每条记录会被 writer 赋予一个严格递增的Entry id,所有的追加操作是非阻塞的,也就是说:第二条记录不用等待第一条记录返回结果就可以发出。已发送到Bookie但未被ack的数据的位置指针叫做Last-Add-Pushed(LAP)。

注意这个模型是以writer为视角,Entry实际上可能会存在多个bookie上,多个bookie对应一个writer。

一致性模型-追加数据时的中间状态:

image.png

伴随着写成功的 ack,writer 不断地更新一个指针叫做 Last-Add-Confirm(LAC),所有 Entry id 小于等于 LAC 的记录保证持久化并复制到大多数副本上,而 LAC 与 LAP(Last-Add-Pushed)之间的记录就是已经发送到 Bookie 上但还未被 ack 的数据。

LAC作为Entry元数据的一部分,通过Entry保存到Bookie中,而Ledger本身并不存储LAC,这样设计的好处是:

  • 不需要频繁更新ZK元数据
  • 因为LAC保证了之前的entry已经持久化成功了,那么在做ledger恢复的时候,只需要从LAC后面开始处理就可以了

LAC由客户端生成,在动态插入Entry的时候,记录的是当前已经ACK成功的Entry ID。比如插入Entry5的时候,LAC是4,那么存储在bookie的Entry5中LAC是4。我们知道Ledger包含了元数据Last Entry ID,当Ledger关闭时,会更新5到Last Entry ID中。

 

读一致性

image.png

所有的 Reader 都可以安全读取 Entry ID 小于或者等于 LAC 的记录,从而保证 reader 不会读取未确认的数据,保证了 reader 之间的一致性

 

写一致性:Fencing机制

image.png

简单来说,Fencing机制用于防止有多个writer(pulsar中即为broker)同时写同一个topic/partition

什么时候会出现多个writer同时写同一个topic呢?在pulsar中,当zk检测到有一个broker1挂掉了,那么会把该broker1拥有的topic所有权转移到另一个broker2。如果broker1实际上没挂掉(类似出现脑裂的情况),那么会出现broker1、broker2同时写同一个topic,对于broker1写入完成的数据,由于topic已经给broker2接管了,在broker2看来并不知道broker1写入了数据,就会出现写入数据的不一致。

那么bookkeeper是如果解决这个问题呢?它引入了Fencing机制,工作机制如下:

  1. 假设Topic X当前拥有者Broker1(B1)不可用(通过zk判断)
  2. 其它broker(B2)将TopicX当前Ledger状态从OPEN修改为IN_RECOVERY
  3. B2向Ledger的当前Fragment的Bookies发送fence信息,并等待Qw-Qa + 1个Bookie响应。收到此响应后Ledger将变成fenced。如果旧的broker仍然处于活跃状态,将无法继续写入,因为无法获得Qa个确认(由于fencing导致异常响应)
  4. 接着B2从Fragment的Bookies获得bookie各自最后的LAC是什么,然后从该位置开始向前读,得到未满足Qw数的Entry(因为可能存在已写入Qa个bookie,但由于broker故障导致bookie数不足Qw的情况)。将这些Entry进行复制,使之达到Qw个Bookie
  5. B2将Ledger的状态更改为CLOSED
  6. B2现在可以创建新的Ledger并接受写入请求。

BookKeeper的fencing特性可以很好的处理Broker脑裂问题。没有脑裂,没有分歧,没有数据丢失。

 

参考链接

  • https://mp.weixin..com/s/CIpCLCxqpLoQVUKz6QeDJQ
  • http://matt33.com/2019/01/28/bk-store-realize/
  • http://bookkeeper.apache.org/docs/latest/getting-started/concepts/
  • https://mp.weixin..com/s/0dkgA8swNPkpcY5H6CU62w
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/228494.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月16日 下午6:53
下一篇 2026年3月16日 下午6:53


相关推荐

  • TextField的高宽autosize[通俗易懂]

    TextField的高宽autosize[通俗易懂]vart_name:TextField=newTextField;trace(t_name.height);trace(t_name.width);这样打印出来的高度都是100PX,是系统默认的。如果不设置可能会让父容器宽高变大,遮挡住别的窗口的鼠标事件。可以简单地使用tf.autoSize=TextFieldAutoSize.LEFT;来解决这样宽高就会根据文本内容大小来…

    2022年5月15日
    42
  • 3步搞定!即梦AI长图海报保姆级教程

    3步搞定!即梦AI长图海报保姆级教程

    2026年3月15日
    2
  • 超详细,手把手 教你 SQLyog 安装教程[通俗易懂]

    超详细,手把手 教你 SQLyog 安装教程[通俗易懂]1.点击SQLyog.exe,如下图2.点击ok,如下图3.点击下一步4.接收协议,点击下一步5.下一步6.更改安装路径,点击安装7.下一步,完成8.注册,需要密钥,名字随意密钥:dd987f34-f358-4894-bd0f-21f3f04be9c19.注册完成,如下图9.新建连接,并随意起个名字10.输入密码,连接数据库11.连接成功之后,就可以使用了…

    2022年5月28日
    1.3K
  • idea自动导包设置

    idea自动导包设置idea 自动导包设置 1 File gt Settings2 说明勾选标注第 1 个选项 IDEA 自动帮我们导入需要用到的包 但是对于那些同名的包 还是需要手动 Alt Enter 进行导入的勾选标注第 2 个选项 IDEA 自动帮我们优化导入的包 比如自动去掉一些没有用到的包

    2026年3月19日
    21
  • IDEA 使用Git回滚到上一个版本

    IDEA 使用Git回滚到上一个版本IDEA 使用 Git 回滚到上一个版本一 版本介绍 pull 到版本 v2 向回到指定版本 v1 二 选择需要回退的版本三 选择自己当前需要的模式 进行版本回退 hard 移动本地库 HEAD 指针 重置暂存区 重置工作区回滚后 本地代码就是你回退版本的代码 soft 回滚后 仅仅是把本地库的指针移动了 而暂存区和你本地的代码是没有做任何改变的 而你上次改动已提交 committed 到本地库的代码显示是绿色即未提交 mixed 移动本地库 HEAD 指针 重置暂存区回滚后 不仅移动了本地库的指针 同

    2026年3月18日
    2
  • 内网渗透神器_内网渗透什么意思

    内网渗透神器_内网渗透什么意思内网渗透-常用工具免杀Mimikatz免杀Mimikatz其实并不只有抓取口令这个功能,它还能够创建票证、票证传递、hash传递、甚至伪造域管理凭证令牌等诸多功能。由于mimikatz的使用说明网上资料很多,这里就不多加介绍了,随着这两年hw行动越来越多,企事业单位也都开始注重内网安全,有预算的会上全套的终端安全、企业版杀软或者EDR,就算没有预算的也会装个360全家桶或者主机卫士之类的,这也导致很多时候你的mimikatz可能都没法拷贝过去或者没有加载执行,拿了台服务器却横向移不动就尴尬了。因为这款工

    2022年8月20日
    14

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号