nimble
Meta 开源的下一代列式存储格式,专为超宽表和机器学习训练数据设计
加载项目详情…
本应用为开源项目,仅供学习研究,请遵守其开源协议。
Meta 开源的下一代列式存储格式,专为超宽表和机器学习训练数据设计
加载项目详情…
本应用为开源项目,仅供学习研究,请遵守其开源协议。
想象这样一个场景:你的机器学习训练集有 5000 个特征列,传统的 Parquet 文件在读取时需要解码整个列,磁盘 I/O 成为瓶颈;而 Meta 内部的特征工程平台早已处理着这样的超宽表(ultra-wide tables),他们需要一个读写速度更快、压缩效率更高的列式存储格式——这就是 Nimble 诞生的背景。
Nimble 由 Meta(原 Facebook)开发,原代号为 "Alpha",定位为 Apache Parquet 和 Apache ORC 的继任者。其设计初衷是解决 Meta 内部超大规模机器学习训练数据存储的几大痛点:Parquet 的元数据组织在宽表场景下效率低下、缺乏对 SIMD/GPU 并行解码的原生支持、扩展机制不够灵活。
项目作者曾在 2024 年公开演讲(YouTube: "Nimble: A new columnar file format"),详细阐述了其设计动机。目前 Nimble 已在 Meta 内部的 Velox 查询引擎中集成使用,Velox 是 Meta 开源的内存计算引擎,被广泛应用于 Presto、Flink、Spark 等大数据系统的底层加速。
Nimble 最大的技术亮点是级联编码(Cascading Encoding)——允许将多个编码器嵌套组合,形成一条编码链。例如对一组浮点数列,可以先用 Delta 编码消除递增趋势,再用 FixedBitWidth 编码压缩差值,理论上可以显著降低存储空间。
目前 Nimble 内置支持 12 种编码器:
| 编码类型 | 适用场景 | 压缩原理 |
|---|---|---|
| Trivial | 任意数据 | 不压缩,原样存储 |
| Constant | 全列相同 | 仅存一个值 |
| MainlyConstant | 大部分相同 | 存共同值 + 例外列表 |
| RLE | 连续重复值 | 存 (值, 连续长度) 对 |
| Dictionary | 低基数字符串 | 字典 + 索引数组 |
| FixedBitWidth | 有界整数 | 按实际位数打包 |
| Varint | 可变长整数 | 变长字节编码 |
| Delta | 有序/趋势数据 | 存储相邻差值 |
| Sentinel | 可空数据 | 用特殊值标记 null |
| Nullable | 可空数据 | 显式 null 位图 |
| SparseBool | 偏斜布尔值 | 仅存少数值位置 |
| Prefix | 字符串 | 公共前缀压缩 |
每个编码器都可以递归地嵌套在其他编码器内部,这套机制叫做 Cascading(级联),是 Nimble 的核心创新点。项目文档中有一张架构图描述了编码器的组合方式,虽然图片无法直接展示,但这个设计思路清晰:编码器是可插拔的插件,通过策略引擎(Encoding Selection Policy)自动为每列选择最优编码组合。
Nimble 的物理文件布局采用 块编码(Block Encoding) 而非流式编码(Stream Encoding)。这意味着解码时的内存占用是可预测的——读取一个数据块所需的内存不会超过块大小,这对在线查询和交互式分析场景非常友好。相比之下,Parquet 的流式解码在处理超宽表时可能导致不可预期的内存峰值。
元数据部分使用 FlatBuffers 而非 Thrift/Protobuf,相比之下 FlatBuffers 的优势在于:无需解包整个协议缓冲区就能直接随机访问字段,对于包含数万个列信息的元数据块来说,这种零拷贝访问方式能大幅减少元数据解析开销。
Nimble 的元数据组织对宽表(数千列)进行了专门优化。每个列簇(column group)和流(stream)的元数据都被压缩在轻量级的索引结构中,查询引擎可以根据元数据直接跳过不相关的列块,实现延迟物化(late materialization)——只读取实际需要的列。
从代码结构看,Nimble 采用分层架构(自底向上):
项目使用 CMake 构建系统,依赖通过 git submodule 管理:
构建流程:make submodules 拉取所有子模块 → make cmake 生成构建文件 → make build 编译。支持 Ninja 加速构建,支持 ccache 缓存。
最佳应用场景:
当前局限性:
parquet-tools),无法直接用 nimble 命令查看文件内容或格式版本Nimble 代表了列式存储格式从"通用档案格式"向"领域定制格式"的演进趋势。Parquet 诞生于 2015 年,当时主流特征列数在数十到数百级别,而 2020 年后大模型和推荐系统的特征维度已飙升至数万。Nimble 的设计者认识到:宽表场景下传统列式格式的元数据开销和编码粒度已不再适用,必须从底层重新设计。
这种"场景驱动格式演进"的思路值得关注。随着 AI 训练数据规模从 GB 级进入 PB 级,存储格式的读写效率将直接影响模型迭代速度。Nimble 的级联编码和块级解码设计,为未来 GPU 直读列式数据奠定了物理基础(文档中提到未来会暴露 SIMD/GPU 解码元数据)。
# 克隆并初始化子模块
git clone https://github.com/facebookincubator/nimble.git
cd nimble
make submodules
# 生成构建文件(需要 Ninja)
make cmake BUILD_DIR=release
# 编译
make build -j$(nproc)
# 运行测试
make unit
构建依赖:CMake >= 3.10、C++ 编译器(GCC 9+ 或 Clang 12+)、Ninja(可选但推荐)。编译产物主要是 libnimble.a 静态库和测试可执行文件,无独立应用程序输出。
总结:Nimble 是一个极具技术深度的底层存储格式创新项目,核心价值在于级联编码和宽表元数据优化,在 ML 训练数据场景下有明确的技术优势。但目前仍处于实验阶段,缺乏稳定性保证和外围工具链,适合作为技术研究和 Velox 集成的参考,而非直接用于生产数据管道。