[笔记] Kudu 存储引擎 | Kudu 文件系统

Block 抽象接口 操作系统把磁盘抽象成文件,Kudu 则在文件之上再加了一层抽象——Block。在 Kudu 中,一列数据、一个 BloomFilter、一份主键索引,最终都变成一个或多个 Block 写入磁盘。Block 是 Kudu 存储引擎与本地文件系统之间的分界线:上层组件只需面对 Block 接口的 Append / Read,不必关心底层是一个独立文件(FileBlockManager)还是日志容器中的一段字节区间(LogBlockManager)。 这种隔离方式与 Unix 的设备抽象思路一致——Unix 内核用 struct file 加上 read/write 函数指针屏蔽底层设备差异;Kudu 用 Block 基类加上虚函数屏蔽底层文件系统的组织差异。 BlockId:Block 的身份标识 每个 Block 都有一个全局唯一的身份标识 BlockId(src/kudu/fs/block_id.h),本质上是一个 64 位无符号整数: 1 2 3 4 class BlockId { private: uint64_t id_; }; BlockId 可以序列化到 protobuf(CopyToPB / FromPB),也可以打印成 16 位十六进制字符串供调试。它是不透明的——上层代码不应假设 ID 的分配规则或数值含义,只需把它当作一把钥匙,用来在 BlockManager 中取回对应的数据。 Block 基类 Block 是所有 Block 的基类,接口极为简洁——只有一个方法: 1 2 3 4 5 class Block { public: virtual ~Block() = default; virtual const BlockId& id() const = 0; }; id() 返回该 Block 的 BlockId。这足以让上层代码通过 ID 引用任何 Block,而不必关心它是可读的还是可写的。Block 的两个子类——WritableBlock 和 ReadableBlock——分别定义了写路径和读路径的完整接口。 ...

发表于:2024-10-23  ·  更新于: 2024-10-23

[笔记] Kudu 存储引擎 | memrowset 实现详解

数据结构 MemRowSet 是 Kudu tablet 中用来暂存新写入数据的内存结构。它的底层存储是一棵并发 B-tree(CBTree),每个叶节点条目存放一个 key-value 对:key 是主键的编码形式(字典序 = 主键逻辑序),value 是一个 MRSRow。 为了支持快照一致性,MemRowSet 从不原地更新已插入的行数据——所有后续变更都以 Mutation 节点的形式挂在行的 redo 链表上,相当于每行自带一条"redo log"。当 MemRowSet 被 flush 到磁盘时,所有内存——包括行数据、Mutation 节点、变长列数据——都从 Arena 中统一释放。 MRSRow MRSRow 是行在 CBTree 中的存储表示。每个 MRSRow 占据一段连续内存,包含一个定长的 Header 和紧随其后的行数据。根据它的构造函数看出其内存布局如下: 1 2 3 4 5 传入的 Slice s 指向的连续内存: |<---------- s.size() ---------->| | Header (24B) | row_data | ↑ ↑ header_ row_slice_ (remove_prefix 后) Header 有三个字段:insertion_timestamp 记录行的插入时间戳,供 MVCC 读取时判断可见性;redo_head 和 redo_tail 分别指向该行 Mutation 链表的头和尾。新插入的行 redo_head/redo_tail 均为 nullptr——它没有任何变更历史。每当一次 UPDATE 或 DELETE 到来,就会在链表尾部追加一个新的 Mutation 节点: ...

发表于:2024-09-24  ·  更新于: 2026-04-15