当前位置: 首页 > article >正文

Apache BookKeeper Ledger 的底层存储机制解析

Apache BookKeeper 的 ledger(账本)是其核心数据存储单元,底层存储机制结合了日志追加(append-only)、分布式存储和容错设计。Ledger 的数据存储在 Bookie 节点的磁盘上,具体实现涉及 Journal(日志)和 Ledger Storage(账本存储)两个部分。以下是 ledger 底层存储数据的详细机制:


Ledger 存储的整体架构

  1. 分布式存储
    • 一个 ledger 的数据分布在多个 Bookie 节点上(由 ensembleSize 定义,例如 3 个节点)。
    • 每个 Bookie 负责存储 ledger 的一部分或全部数据(取决于 writeQuorum 配置)。
  2. 两阶段存储
    • Journal:实时记录写入操作的事务日志,确保数据持久化。
    • Ledger Storage:长期存储账本数据,优化读取性能。
  3. 文件系统
    • 数据直接存储在 Bookie 节点的本地文件系统中(例如 ext4、XFS),没有额外的数据库层。

底层存储的实现细节

1. Journal(日志)
  • 作用
    • Journal 是 ledger 数据写入的第一步,用于保证数据持久性和一致性。
    • 每次写入条目(entry)时,先追加到 Journal,确保即使系统崩溃也能恢复。
  • 存储位置
    • 配置项 journalDirectory 指定路径(例如 /bookkeeper/journal)。
    • 每个 Bookie 节点独立维护自己的 Journal。
  • 文件结构
    • Journal 由多个日志文件组成,按时间或大小滚动(rollover)。
    • 文件名格式:journal.<timestamp>(例如 journal.1698765432100)。
    • 每个文件是一个顺序追加的二进制文件。
  • 写入过程
    1. 客户端发送条目到 Bookie。
    2. Bookie 将条目序列化为二进制格式,包含:
      • Ledger ID:账本标识。
      • Entry ID:条目序列号。
      • Data:实际数据内容。
    3. 追加到当前 Journal 文件。
    4. 可配置 journalSyncData=true(默认),调用 fsync 强制刷盘,确保数据持久化。
    5. 返回确认(ACK)给客户端。
  • 性能优化
    • Journal 使用顺序写入,适合高吞吐量。
    • 建议将 journalDirectory 放在高速磁盘(例如 SSD)上。
2. Ledger Storage(账本存储)
  • 作用
    • Journal 确认后,数据异步写入 Ledger Storage,用于长期存储和读取。
  • 存储位置
    • 配置项 ledgerDirectories 指定路径(例如 /bookkeeper/ledgers)。
    • 可以配置多个目录(例如 /disk1/ledgers, /disk2/ledgers),分散 I/O 负载。
  • 文件结构
    • Ledger 数据按 ledger 分片存储,目录结构:
    • /bookkeeper/ledgers/
      ├── current/          # 当前活跃的账本文件
      │   ├── 00000001.log  # Ledger ID 1 的数据文件
      │   ├── 00000002.log  # Ledger ID 2 的数据文件
      ├── recovered/        # 崩溃恢复后的文件
      └── compacted/        # 压缩后的文件(可选)

    • 每个 .log 文件对应一个 ledger,包含该 ledger 的所有条目。
  • 写入过程
    1. Journal 写入成功后,条目放入内存缓冲区(EntryLogger)。
    2. 缓冲区满或达到刷新间隔(ledgerStorage_flushInterval)时,异步写入 .log 文件。
    3. 数据按 Entry ID 顺序存储,文件格式为二进制。
  • 索引
    • 为了快速定位条目,BookKeeper 维护一个索引。
    • 配置项 indexDirectories 指定路径(默认与 ledgerDirectories 相同)。
    • 默认使用文件系统索引(FileInfo),可选配置 RocksDB(dbStorage_rocksDB_* 参数)提高性能。
    • 索引记录每个 Entry ID 在 .log 文件中的偏移量。
3. 数据分布
  • Ensemble
    • 一个 ledger 的数据分布在 ensembleSize 个 Bookie 上。
    • 例如,ensembleSize=3,数据可能存储在 bookie1、bookie2、bookie3。
  • Write Quorum
    • 每次写入,数据完整存储在 writeQuorumSize 个 Bookie 上。
    • 如果 writeQuorum < ensembleSize,不同条目可能分布在不同的 Bookie 子集。
  • 副本
    • 每个条目在多个 Bookie 上有副本(由 writeQuorum 控制),提供容错性。

数据写入的完整流程

以 ensembleSize=3, writeQuorum=3, ackQuorum=2 为例:

  1. 客户端
    • 创建 ledger,分配 Ledger ID=1,选择 bookie1、bookie2、bookie3 作为 ensemble。
    • 发送条目 entry1 到 3 个 Bookie。
  2. Bookie
    • bookie1:写入 /journal/journal.<timestamp>,返回 ACK。
    • bookie2:写入 /journal/journal.<timestamp>,返回 ACK。
    • bookie3:写入 /journal/journal.<timestamp>,返回 ACK(可能稍慢)。
    • 客户端收到 2 个 ACK(满足 ackQuorum=2),写入成功。
  3. 异步存储
    • 每个 Bookie 将 entry1 从 Journal 移到 /ledgers/current/00000001.log。
    • 更新索引,记录 entry1 的偏移量。

数据读取

  • 读取流程
    1. 客户端指定 Ledger ID 和 Entry ID。
    2. Bookie 从索引查找条目位置。
    3. 从 .log 文件读取数据返回。
  • 容错
    • 如果某个 Bookie 不可用,客户端从其他副本读取(需要至少 ackQuorum 个副本可用)。

存储特性

  1. 追加式存储
    • Ledger 只支持追加写入(append-only),不支持修改或删除。
    • 删除 ledger 需要关闭并通过 ZooKeeper 删除元数据。
  2. 纠删码(Erasure Coding)
    • 默认不使用纠删码,而是完整副本存储。
    • 可通过配置启用纠删码(实验性功能),减少存储开销。
  3. 持久性
    • Journal 的 fsync 保证写入持久化。
    • Ledger Storage 异步写入,依赖 Journal 恢复一致性。

崩溃恢复

  • Journal 回放
    • Bookie 重启时,检查 Journal 文件,恢复未写入 Ledger Storage 的条目。
    • 恢复后,数据移到 recovered/ 目录。
  • 一致性
    • 只要 ackQuorum 个 Bookie 存活,数据不会丢失。

性能优化

  1. 分离存储
    • 将 journalDirectory 和 ledgerDirectories 放在不同磁盘(例如 SSD 和 HDD),提高 I/O 性能。
  2. 批量写入
    • Journal 支持批量 fsync,减少磁盘同步开销。
  3. 索引优化
    • 使用 RocksDB 替代默认文件索引,加速查找。

总结

Ledger 的底层存储机制:

  • Journal:顺序写入事务日志,保证持久性,存储在 journalDirectory。
  • Ledger Storage:异步存储账本数据,分布在 ledgerDirectories 的 .log 文件中。
  • 索引:记录条目偏移量,存储在 indexDirectories。
  • 分布式:数据按 ensembleSize 分布在多个 Bookie,副本数由 writeQuorum 控制。

这种设计结合了高吞吐量(顺序写入)、低延迟(异步存储)和容错性(多副本),非常适合分布式日志存储需求。你的 Go Demo 数据最终存储在 3 个 Bookie 的 Journal 和 Ledger 文件中,具体路径取决于 Docker Compose 的卷配置

相关文章:

Apache BookKeeper Ledger 的底层存储机制解析

Apache BookKeeper 的 ledger&#xff08;账本&#xff09;是其核心数据存储单元&#xff0c;底层存储机制结合了日志追加&#xff08;append-only&#xff09;、分布式存储和容错设计。Ledger 的数据存储在 Bookie 节点的磁盘上&#xff0c;具体实现涉及 Journal&#xff08;日…...

从代码上深入学习GraphRag

网上关于该算法的解析都停留在大概流程上&#xff0c;但是具体解析细节未知&#xff0c;由于代码是PipeLine形式因此阅读起来比较麻烦&#xff0c;本文希望通过阅读项目代码来解析其算法的具体实现细节&#xff0c;特别是如何利用大模型来完成图谱生成和检索增强的实现细节。 …...

通俗地讲述DDD的设计

通俗地讲述DDD的设计 前言为什么要使用DDDDDD架构分层重构实践关键问题解决方案通过​​领域事件机制​​解耦服务依赖&#xff1a;防止逻辑下沉 领域划分电商场景下的领域划分 结语完结撒花&#xff0c;如有需要收藏的看官&#xff0c;顺便也用发财的小手点点赞哈&#xff0c;…...

【Redis】通用命令

使用者通过redis-cli客户端和redis服务器交互&#xff0c;涉及到很多的redis命令&#xff0c;redis的命令非常多&#xff0c;我们需要多练习常用的命令&#xff0c;以及学会使用redis的文档。 一、get和set命令&#xff08;最核心的命令&#xff09; Redis中最核心的两个命令&…...

网络安全技术文档

网络安全技术文档 1. 概述 网络安全是指通过技术手段和管理措施&#xff0c;保护网络系统的硬件、软件及其数据不受偶然或恶意破坏、更改、泄露&#xff0c;确保系统连续可靠运行&#xff0c;网络服务不中断。 2. 常见网络威胁 2.1 攻击类型 DDoS攻击&#xff1a;分布式拒…...

微前端随笔

✨ single-spa&#xff1a; js-entry 通过es-module 或 umd 动态插入 js 脚本 &#xff0c;在主应用中发送请求&#xff0c;来获取子应用的包&#xff0c; 该子应用的包 singleSpa.registerApplication({name: app1,app: () > import(http://localhost:8080/app1.js),active…...

【36期获取股票数据API接口】如何用Python、Java等五种主流语言实例演示获取股票行情api接口之沪深A股当天逐笔大单交易数据及接口API说明文档

​ 在量化分析领域&#xff0c;实时且准确的数据接口是成功的基石。经过多次实际测试&#xff0c;我将已确认可用的数据接口分享给正在从事量化分析的朋友们&#xff0c;希望能够对你们的研究和工作有所帮助&#xff0c;接下来我会用Python、JavaScript&#xff08;Node.js&…...

C++中的浅拷贝和深拷贝

浅拷贝只是将变量的值赋予给另外一个变量&#xff0c;在遇到指针类型时&#xff0c;浅拷贝只会把当前指针的值&#xff0c;也就是该指针指向的地址赋予给另外一个指针&#xff0c;二者指向相同的地址&#xff1b; 深拷贝在遇到指针类型时&#xff0c;会先将当前指针指向地址包…...

二叉树与红黑树核心知识点及面试重点

二叉树与红黑树核心知识点及面试重点 一、二叉树 (Binary Tree) 1. 基础概念 定义&#xff1a;每个节点最多有两个子节点&#xff08;左子节点和右子节点&#xff09; 术语&#xff1a; 根节点&#xff1a;最顶层的节点 叶子节点&#xff1a;没有子节点的节点 深度&#xf…...

GitHub 趋势日报 (2025年04月01日)

GitHub 趋势日报 (2025年04月01日) 本日报由 TrendForge 系统生成 https://trendforge.devlive.org/ &#x1f4c8; 今日整体趋势 Top 10 排名项目名称项目描述今日获星语言1punkpeye/awesome-mcp-serversA collection of MCP servers.⭐ 3280未指定2th-ch/youtube-musicYouTu…...

Java的SeleniumChromeDriver的常用方法

启动和关闭浏览器&#xff1a; driver.get(url)&#xff1a;打开指定的URL。driver.quit()&#xff1a;关闭浏览器并结束ChromeDriver会话。 元素定位&#xff1a; driver.findElement(By.id("elementId"))&#xff1a;通过元素的ID定位。driver.findElement(By.cl…...

字符串、列表、元组、字典

字符串 双引号或者单引号中的数据&#xff0c;就是字符串 字符串输入 之前在学习input的时候&#xff0c;通过它能够完成从键盘获取数据&#xff0c;然后保存到指定的变量中&#xff1b; 注意&#xff1a;input获取的数据&#xff0c;都以字符串的方式进行保存&#xff0c;即…...

【GEE学习笔记】报错解决:“Image.select: Band pattern ‘QA60‘ did not match any bands”

【GEE学习笔记】报错解决&#xff1a;“Image.select: Band pattern ‘QA60’ did not match any bands” 【GEE学习笔记】报错解决&#xff1a;“Image.select: Band pattern ‘QA60’ did not match any bands” 文章目录 【GEE学习笔记】报错解决&#xff1a;“Image.selec…...

AI可以赋能的三农产品、机械与服务

三农赛道涵盖农业、农村和农民相关的产品与服务&#xff0c;涉及农资、农业机械、智能设备、农产品加工及数字化服务等多个领域。随着人工智能&#xff08;AI&#xff09;技术的飞速发展&#xff0c;AI正在通过赋能农业的生产、管理、销售等各个环节&#xff0c;推动传统农业向…...

ngx_timezone_update

定义在 src\os\unix\ngx_time.c void ngx_timezone_update(void) { #if (NGX_FREEBSD)if (getenv("TZ")) {return;}putenv("TZUTC");tzset();unsetenv("TZ");tzset();#elif (NGX_LINUX)time_t s;struct tm *t;char buf[4];s tim…...

车载诊断架构 --- 整车重启先后顺序带来的思考

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 周末洗了一个澡,换了一身衣服,出了门却不知道去哪儿,不知道去找谁,漫无目的走着,大概这就是成年人最深的孤独吧! 旧人不知我近况,新人不知我过…...

GESP C++三级 知识点讲解

C编程三级标准 (一)知识点详述 (1)了解二进制数据编码:原码、反码、补码。 (2)掌握数据的进制转换:二进制、八进制、十进制、十六进制。 (3)掌握位运算:与(&)、或(|)、非(~)、异或(^)、左移(<<)、右移(>>)的基本使用方法及原理。 (4)了解算法的概念与描述&…...

前端 vs 后端:技术分工详解——从用户界面到系统逻辑的全解析

前端&#xff08;Frontend&#xff09; 和 后端&#xff08;Backend&#xff09; 是软件开发中两个核心概念&#xff0c;分别对应用户直接交互的部分和系统背后的逻辑处理部分。它们共同构成完整的应用程序&#xff0c;但分工不同。 目录 一、前端&#xff08;Frontend&#xf…...

Redis 除了数据类型外的核心功能 的详细说明,包含事务、流水线、发布/订阅、Lua 脚本的完整代码示例和表格总结

以下是 Redis 除了数据类型外的核心功能 的详细说明&#xff0c;包含事务、流水线、发布/订阅、Lua 脚本的完整代码示例和表格总结&#xff1a; 1. Redis 事务&#xff08;Transactions&#xff09; 功能描述 事务通过 MULTI 和 EXEC 命令将一组命令打包执行&#xff0c;保证…...

JavaScript智能对话机器人——企业知识库自动化

引言 内部知识管理常面临信息分散、查找困难的问题。本文将使用Node.js和虎跃办公的智能对话API&#xff0c;构建企业级知识问答机器人&#xff0c;支持自然语言查询和自动学习。 核心技术 自然语言处理&#xff08;NLP&#xff09;意图识别机器学习模型微调REST API集成 代…...

JS实现AES和DES

目录 目标 概述 DES AES 实战 JS实现DES JS实现AES 目标 了解AES和DES的特点并用JS实现。 概述 DES 翻译过来叫数据加密标准。它有5种加密模式&#xff08;CTR、OFB、CFB、CBC、ECB&#xff09;&#xff0c;在JS中&#xff0c;不同加密模式语法结构几乎一致&#xff0c…...

【C++11(下)】—— 我与C++的不解之缘(三十二)

前言 随着 C11 的引入&#xff0c;现代 C 语言在语法层面上变得更加灵活、简洁。其中最受欢迎的新特性之一就是 lambda 表达式&#xff08;Lambda Expression&#xff09;&#xff0c;它让我们可以在函数内部直接定义匿名函数。配合 std::function 包装器 使用&#xff0c;可以…...

Windows 10/11系统优化工具

家庭或工作电脑使用时间久了&#xff0c;会出现各种各样问题&#xff0c;今天给大家推荐一款专为Windows 10/11系统设计的全能优化工具&#xff0c;该软件集成了超过40项专业级实用程序&#xff0c;可针对系统性能进行深度优化、精准调校、全面清理、加速响应及故障修复。通过系…...

浅谈在HTTP中GET与POST的区别

从 HTTP 报文来看&#xff1a; GET请求方式将请求信息放在 URL 后面&#xff0c;请求信息和 URL 之间以 &#xff1f;隔开&#xff0c;请求信息的格式为键值对&#xff0c;这种请求方式将请求信息直接暴露在 URL 中&#xff0c;安全性比较低。另外从报文结构上来看&#xff0c…...

LightRAG实战:轻松构建知识图谱,破解传统RAG多跳推理难题

作者&#xff1a;后端小肥肠 &#x1f34a; 有疑问可私信或评论区联系我。 &#x1f951; 创作不易未经允许严禁转载。 姊妹篇&#xff1a; 2025防失业预警&#xff1a;不会用DeepSeek-RAG建知识库的人正在被淘汰_deepseek-embedding-CSDN博客 从PDF到精准答案&#xff1a;Coze…...

C++多线程编码二

1.lock和try_lock lock是一个函数模板&#xff0c;可以支持多个锁对象同时锁定同一个&#xff0c;如果其中一个锁对象没有锁住&#xff0c;lock函数会把已经锁定的对象解锁并进入阻塞&#xff0c;直到多个锁锁定一个对象。 try_lock也是一个函数模板&#xff0c;尝试对多个锁…...

垃圾回收——三色标记法(golang使用)

三色标记法(tricolor mark-and-sweep algorithm)是传统 Mark-Sweep 的一个改进&#xff0c;它是一个并发的 GC 算法&#xff0c;在Golang中被用作垃圾回收的算法&#xff0c;但是也会有一个缺陷&#xff0c;可能程序中的垃圾产生的速度会大于垃圾收集的速度&#xff0c;这样会导…...

Linux学习笔记——零基础详解:什么是Bootloader?U-Boot启动流程全解析!

零基础详解&#xff1a;什么是Bootloader&#xff1f;U-Boot启动流程全解析&#xff01; 一、什么是Bootloader&#xff1f;&#x1f4cc; 举个例子&#xff1a; 二、U-Boot 是什么&#xff1f;三、U-Boot启动过程&#xff1a;分为两个阶段&#x1f539; 第一阶段&#xff08;汇…...

Windows环境下开发pyspark程序

Windows环境下开发pyspark程序 一、环境准备 1.1. Anaconda/Miniconda&#xff08;Python环境&#xff09; 如果不怕包的版本管理混乱&#xff0c;可以直接使用已有的Python环境。 需要安装anaconda/miniconda&#xff08;python3.8版本以上&#xff09;&#xff1a;Anaconda…...

thinkphp8.0上传图片到阿里云对象存储(oss)

1、开通oss,并获取accessKeyId、accessKeySecret <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><tit…...