跳到主要内容

宽表存储格式 V3

提示

本功能自 Apache Doris 4.1.0 版本开始支持。在建表 PROPERTIES 中设置 "storage_format" = "V3" 即可启用。

宽表存储格式 V3 针对列数极多的表对 Segment 元数据布局做了重构。通过将列元数据从 Footer 中拆出、按需加载,显著降低 Segment 打开延迟与内存占用。

适用场景

如果你的查询观察到 "Segment 打开"阶段非常慢、内存占用异常高,且符合下表中任一场景,建议启用 V3 存储格式:

用户场景典型表现是否推荐 V3
宽表查询(几百至几千列)即使只查少数列,Segment 打开仍占用大量时间和内存推荐
VARIANT 列的表子列动态展开后,实际参与存储的列数远超表面定义推荐
部署在对象存储 / 分层存储数据放在 S3、OSS 等远端存储上,冷查询延迟敏感推荐
普通表(几十列以内)Segment 打开开销可忽略无需切换

典型痛点示例:在一张 7,000 列、共 10,000 个 Segment 的宽表上,打开 Segment 需要约 65 秒,过程中峰值内存占用达 60 GB。这部分开销与查询是否真的用到这些列无关,属于纯粹的"上车成本"。

问题根因

旧格式把所有列的元数据(ColumnMetaPB)集中打包在 Segment 文件末尾的 Footer 里,导致:

  1. 元数据全量加载:打开 Segment 必须先把整个 Footer 反序列化,哪怕查询只用到两列也要付全部代价。
  2. Footer 体积膨胀:当列数到达几千时,Footer 本身就能膨胀到几 MB。
  3. 远端存储放大效应:对象存储上的网络延迟与高读取成本使上述开销进一步被放大。

换言之,即使一条 SQL 只查询 2 个列,Doris 也要先把这个 Segment 内所有列的元数据全部读到内存里、反序列化后才能开始扫描。列越多、Segment 越多,这部分开销越夸张。

V3 关键优化

V3 从三个维度对存储格式进行重构:

优化一:列元数据按需加载

V3 把列元数据从 Footer 中拆出,放到文件中独立的区域,Footer 只保留指向各列元数据的轻量指针。

宽表存储格式 — Segment 文件布局对比

打开 Segment 时,系统只读一个精简的 Footer;真正用到哪些列,再去拉取对应列的元数据。这是宽表场景下性能提升的主要来源,在对象存储上尤为明显。

优化二:数值类型默认使用 Plain 编码

V3 将 INTBIGINT 等数值类型的默认编码从 BitShuffle 切换为 PLAIN_ENCODING(原始二进制)。配合 LZ4 / ZSTD 压缩后,在大批量扫描时读取更快、CPU 开销更低。

优化三:字符串使用更紧凑的 Plain 编码

针对字符串和 JSONB,V3 引入 BINARY_PLAIN_ENCODING_V2,采用 [长度(varuint)][原始数据] 的流式布局,去掉了旧编码尾部需要的偏移表,存储更紧凑。

实测效果

测试条件:一张 7,000 列的宽表,共 10,000 个 Segment。

宽表存储格式 — 元数据打开效率

指标旧格式V3 格式提升幅度
Segment 打开时间65 s4 s快 16 倍
打开时内存占用60 GB< 1 GB降低 60 倍

如何启用

在建表语句的 PROPERTIES 中显式指定 storage_formatV3

CREATE TABLE table_v3 (
id BIGINT,
name VARCHAR(128),
attrs VARIANT
)
DISTRIBUTED BY HASH(id) BUCKETS 32
PROPERTIES (
"storage_format" = "V3"
);

适用建议

  • 建议启用:列数较多(几百及以上)的宽表、含 VARIANT 列的表、部署在对象存储或分层存储上的表。
  • 无需切换:列数较少(几十列以内)的普通表,旧格式已经足够。

相关文档

  • 数据压缩:了解 V3 在数值与字符串类型上的编码与压缩协同优化。