流式数据湖Paimon探秘之旅 (四) FileStore存储引擎核心

第4章:FileStore存储引擎核心

FileStore是什么?

FileStore 是Paimon的存储引擎实现,负责将Table层的抽象操作转化为具体的文件操作。

关键关系:

scss 复制代码
Table (高级API)
  ↓
FileStore (存储引擎)
  ↓
文件系统 (物理存储)

FileStore的两个实现

4.1 FileStore接口与抽象

核心方法
java 复制代码
FileStore<T> {
  // 扫描操作
  FileStoreScan newScan()
  
  // 读写操作
  SplitRead<T> newRead()
  FileStoreWrite<T> newWrite(user)
  FileStoreCommit newCommit(user)
  
  // 管理
  SnapshotManager snapshotManager()
  ChangelogManager changelogManager()
  
  // 配置
  CoreOptions options()
  BucketMode bucketMode()
}
两种实现
实现 适用表类型 核心特性 内部结构
KeyValueFileStore 主键表 支持更新删除 LSM树
AppendOnlyFileStore 追加表 高吞吐追加 简单序列化存储

4.2 AppendOnlyFileStore实现

什么(What):追加表存储如何工作?

追加表只支持INSERT操作,实现非常简洁:

kotlin 复制代码
写入流程:
Record1 → 写入内存缓冲
Record2 → 写入内存缓冲
Record3 → 写入内存缓冲
...
(缓冲满或提交时)
  → 生成data-file-1.parquet
  → 生成Manifest条目
  → 提交Snapshot
为什么(Why):为什么追加表这么简单?
  • 无去重:每条记录都是新增,不需要处理重复
  • 无版本管理:不需要合并多个版本的同一主键
  • 高性能:直接顺序写入,零开销
怎样(How):实现细节

核心类:

java 复制代码
AppendOnlyFileStore {
  newWrite() {
    return AppendFileStoreWrite {
      write(record) {
        // 直接写入内存缓冲
        writeBuffer.append(record);
        
        // 缓冲满或达到时间限制时
        if (writeBuffer.isFull()) {
          flushBuffer(); // 生成文件
        }
      }
      
      prepareCommit() {
        flushBuffer(); // 最后刷新
        return committables; // 返回待提交的文件列表
      }
    }
  }
}

性能特点:

scss 复制代码
吞吐量:高(直接追加)
延迟:低(避免排序和去重)
存储空间:需要定期清理过期数据

4.3 KeyValueFileStore实现

什么(What):主键表存储如何工作?

主键表需要处理主键重复和版本管理:

ini 复制代码
写入流程:
(user_id=1, name='Alice', age=25)  ← 首次出现
  → 写入LSM树

(user_id=1, name='Alice', age=26)  ← 同一主键
  → LSM树自动合并(新版本覆盖旧版本)

(user_id=2, name='Bob', age=30)    ← 新主键
  → 写入LSM树

查询结果:
  user_id=1: age=26 (最新版本)
  user_id=2: age=30
为什么(Why):为什么需要LSM树?

主键表需要:

  1. 高效的写入:支持大量小批量的UPDATE/DELETE
  2. 有序扫描:按主键有序存储便于查询
  3. 自动去重:同一主键只保留最新版本

LSM树的优势:

  • 顺序写:所有数据先写MemTable(内存),再合并到磁盘
  • 分层存储:新数据在高层,旧数据在低层
  • 异步压缩:后台自动合并文件,不阻塞写入
怎样(How):LSM树的简化模型
scss 复制代码
写入阶段:
├─ MemTable (L0)         ← 新数据先进入MemTable
│  └─ sorted by key
├─ SSTable1 (L1)         ← 定期从L0刷入L1
├─ SSTable2 (L1)
├─ SSTable3 (L2)         ← 较旧的数据
└─ SSTable4 (L3)         ← 最旧的数据

读取流程:
查询 key=1001
  ↓ 先查MemTable(最新)
  ↓ 再查L1的SSTable
  ↓ 再查L2的SSTable
  → 找到数据,返回最新版本

相比B树的优势:

操作 LSM树 B树
随机写 快(转化为顺序写) 快但有随机I/O
顺序读 很快 很快
点查询 中等(需查多层)
范围查询 快(有序存储)
压缩开销 有(后台进行)

4.4 BucketMode分桶策略

三种分桶模式回顾
java 复制代码
// 模式1:固定桶
options("bucket", "4")
→ hash(primary_key) % 4 决定桶号

// 模式2:动态桶
options("bucket", "-1")
→ 初始4个桶,自动分裂增加

// 模式3:无感知桶(仅追加表)
options("bucket", "-1")
→ 追加表专用,不需要主键分布
分桶对存储引擎的影响

单个桶的LSM树:

scss 复制代码
FileStore(bucket-0)
  ├─ MemTable
  ├─ Level-0
  │  ├─ SSTable-0-1
  │  └─ SSTable-0-2
  ├─ Level-1
  │  └─ SSTable-0-3
  └─ Level-2
     └─ SSTable-0-4

FileStore(bucket-1)
  ├─ MemTable
  ├─ Level-0
  │  ├─ SSTable-1-1
  │  └─ SSTable-1-2
  └─ ...

并行写入:

diff 复制代码
写线程1 → bucket-0 → LSM树0 (独立)
写线程2 → bucket-1 → LSM树1 (独立)
写线程3 → bucket-2 → LSM树2 (独立)

优点:
- 三个线程互不竞争
- 可以真正并行写入
- 吞吐量 ≈ 线程数 × 单线程吞吐量

总结:FileStore的核心设计

灵活的抽象层

sql 复制代码
追加表 ─────────────── 简单高效
       FileStore接口
主键表 ─────────────── 功能丰富(支持UPDATE/DELETE)

两种表的选择指南

sql 复制代码
选择追加表如果:
✓ 仅追加日志/事件
✓ 无须去重
✓ 追求最大吞吐量

选择主键表如果:
✓ 有主键、支持更新
✓ 需要去重
✓ 支持DELETE操作

相关代码

  • FileStore接口:paimon-core/src/main/java/org/apache/paimon/FileStore.java
  • KeyValueFileStore:paimon-core/src/main/java/org/apache/paimon/KeyValueFileStore.java
  • AppendOnlyFileStore:paimon-core/src/main/java/org/apache/paimon/AppendOnlyFileStore.java
相关推荐
n***786840 分钟前
PostgreSQL 中进行数据导入和导出
大数据·数据库·postgresql
语落心生1 小时前
流式数据湖Paimon探秘之旅 (三) Catalog体系深度解析
大数据
语落心生1 小时前
流式数据湖Paimon探秘之旅 (六) 提交流程与事务保证
大数据
梦里不知身是客111 小时前
容量调度器
大数据
跨境海外仓小秋1 小时前
仓库如何实现自动汇总订单波次?TOPWMS波次规则助力海外仓拣货效率翻倍
大数据
民乐团扒谱机1 小时前
【微实验】携程评论C#爬取实战:突破JavaScript动态加载与反爬虫机制
大数据·开发语言·javascript·爬虫·c#
涤生大数据1 小时前
Spark分桶表实战:如何用分桶减少 40%+ 计算时间
大数据·sql·spark·分桶表·大数据校招·大数据八股
武子康1 小时前
大数据-170 Elasticsearch 7.3.0 三节点集群实战:目录/参数/启动到联机
大数据·后端·elasticsearch
G皮T2 小时前
【Elasticsearch】索引状态管理(ISM)实战(万字长文)
大数据·elasticsearch·搜索引擎·ilm·ism·索引状态管理·索引生命周期管理