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

深入解析Doris ZoneMap索引机制

ZoneMapIndex 实现分析

zone_map_index.cpp 实现了 Apache Doris 中的 ZoneMap 索引机制。ZoneMap 是一种轻量级索引,用于记录数据区域(zone)的最小值、最大值以及 NULL 值分布信息,实现快速的数据过滤和查询优化。

核心数据结构

  1. ZoneMap 结构体ZoneMap):

    • min_value/max_value: 区域的最小/最大值
    • has_null/has_not_null: NULL 值分布标记
    • pass_all: 是否包含所有值的标记
  2. ZoneMapPB protobufZoneMapPB):

    gensrc\proto\segment_v2.proto
  3. message ZoneMapPB {optional bytes min = 1;           // 最小非NULL值optional bytes max = 2;           // 最大非NULL值  optional bool has_null = 3;       // 是否包含NULL值optional bool has_not_null = 4;   // 是否包含非NULL值optional bool pass_all = 5;       // 是否包含所有值
    }
    

为什么

1. 查询性能优化

ZoneMap 索引的核心价值在于快速数据过滤。在执行查询时,可以通过比较查询条件与 ZoneMap 中的 min/max 值,快速判断某个数据区域是否可能包含目标数据:

  • 区域裁剪(Zone Pruning):如果查询条件的值不在 [min, max] 范围内,则该区域不可能包含目标数据,可以直接跳过
  • NULL 优化:通过 has_null/has_not_null 标记,可以快速处理 IS NULL/IS NOT NULL 查询

2. 存储效率

相比其他索引(如 B+ 树索引),ZoneMap 的存储开销极小:

  • 每个区域只需存储 min/max 两个值和少量标记位
  • 不需要维护复杂的树结构
  • 内存占用低,适合大规模数据

3. 层级优化

实现了两级 ZoneMap 架构:

  • 页面级 ZoneMap:每个数据页面的统计信息
  • 段级 ZoneMapZoneMapIndexPB):整个段的聚合统计信息

这种设计支持多级过滤:先通过段级 ZoneMap 快速判断整个段是否可能包含目标数据,再通过页面级 ZoneMap 进行精确过滤。

怎么做

都是be\src\olap\rowset\segment_v2\zone_map_index.cpp 定义的

1. 写入流程(ZoneMapIndexWriter)

TypedZoneMapIndexWriter 实现了写入逻辑:

数据收集阶段
void add_values(const void* values, size_t count) override {if (count > 0) {_page_zone_map.has_not_null = true;}// 计算最小值和最大值auto [min, max] = std::minmax_element(vals, vals + count);if (unaligned_load<ValType>(min) < unaligned_load<ValType>(_page_zone_map.min_value)) {_field->type_info()->direct_copy_may_cut(_page_zone_map.min_value,reinterpret_cast<const void*>(min));}// 类似处理最大值...
}
页面刷新阶段(flush

 

Status flush() override {// 1. 更新段级ZoneMap(聚合统计)if (_field->compare(_segment_zone_map.min_value, _page_zone_map.min_value) > 0) {_field->type_info()->direct_copy_may_cut(_segment_zone_map.min_value,_page_zone_map.min_value);}// 2. 序列化页面ZoneMapZoneMapPB zone_map_pb;_page_zone_map.to_proto(&zone_map_pb, _field);// 3. 存储到IndexedColumn_values.push_back(std::move(serialized_zone_map));return Status::OK();
}
完成阶段(finish

 

Status finish(io::FileWriter* file_writer, ColumnIndexMetaPB* index_meta) override {// 1. 存储段级ZoneMap到元数据_segment_zone_map.to_proto(meta->mutable_segment_zone_map(), _field);// 2. 使用IndexedColumnWriter写入所有页面ZoneMapIndexedColumnWriter writer(options, type_info, file_writer);for (auto& value : _values) {RETURN_IF_ERROR(writer.add(&value_slice));}return writer.finish(meta->mutable_page_zone_maps());
}

2. 读取流程(ZoneMapIndexReader)

ZoneMapIndexReader 负责读取:

加载阶段(load
Status load(bool use_page_cache, bool kept_in_memory,OlapReaderStatistics* index_load_stats = nullptr) {return _load_once.call([this, ...] {return _load(use_page_cache, kept_in_memory, std::move(_page_zone_maps_meta), index_load_stats);});
}
实际加载逻辑(_load
Status _load(...) {// 1. 创建IndexedColumnReader读取ZoneMap数据IndexedColumnReader reader(_file_reader, *page_zone_maps_meta);RETURN_IF_ERROR(reader.load(...));// 2. 读取所有页面ZoneMap并解析for (int i = 0; i < reader.num_values(); ++i) {vectorized::MutableColumnPtr column = vectorized::ColumnString::create();RETURN_IF_ERROR(iter.seek_to_ordinal(i));RETURN_IF_ERROR(iter.next_batch(&num_read, column));// 解析protobuf格式的ZoneMapif (!_page_zone_maps[i].ParseFromArray(column->get_data_at(0).data,column->get_data_at(0).size)) {return Status::Corruption("Failed to parse zone map");}}
}

3. 类型系统支持

实现了模板化的 ZoneMap 索引,支持所有基本数据类型:

#define APPLY_FOR_PRIMITITYPE(M) \M(TYPE_TINYINT)              \M(TYPE_SMALLINT)             \M(TYPE_INT)                  \M(TYPE_BIGINT)               \M(TYPE_LARGEINT)             \M(TYPE_FLOAT)                \M(TYPE_DOUBLE)               \// ... 更多类型

通过模板特化(TypedZoneMapIndexWriter)为每种类型提供最优的实现:

template <PrimitiveType Type>
class TypedZoneMapIndexWriter final : public ZoneMapIndexWriter {using ValType = PrimitiveTypeTraits<Type>::StorageFieldType;// 类型特定的优化实现
};

4. 存储格式优化

IndexedColumn 存储

页面级 ZoneMap 使用 IndexedColumnWriter 存储:

const auto* type_info = get_scalar_type_info<FieldType::OLAP_FIELD_TYPE_BITMAP>();
IndexedColumnWriterOptions options;
options.write_ordinal_index = true;  // 支持按序号访问
options.write_value_index = false;   // 不需要值索引
options.encoding = EncodingInfo::get_default_encoding(type_info, false);
options.compression = NO_COMPRESSION; // 当前不压缩
内存管理

使用 vectorized::Arena 进行内存管理:

vectorized::Arena _arena;
// min/max 值内存由 Arena 管理,避免频繁内存分配
_page_zone_map.min_value = _field->allocate_zone_map_value(_arena);

5. 性能优化策略

延迟加载(Lazy Loading)

DorisCallOnce<Status> _load_once;
// 确保ZoneMap只加载一次,且在实际需要时才加载

批处理优化

在 add_values 中,使用 std::minmax_element 一次性处理整个批次:

auto [min, max] = std::minmax_element(vals, vals + count);

 

总结

ZoneMapIndex 通过轻量级统计信息实现了高效的数据过滤,其核心优势在于:

  1. 存储高效:每个区域只需存储 min/max 和少量标记
  2. 查询快速:支持基于范围的快速区域裁剪
  3. 架构清晰:两级 ZoneMap 支持多级过滤优化
  4. 类型丰富:模板化设计支持所有基本数据类型
  5. 实现优化:延迟加载、批处理、内存池等性能优化

这种设计使得 Doris 能够在不显著增加存储开销的前提下,大幅提升范围查询和过滤查询的性能,是列式存储系统中索引设计的优秀实践。

页面级别的zone_map

页面级别的zone_map是指为每个数据页面(Data Page)单独维护的zone_map索引,它记录了该页面内数据的最小值、最大值、是否存在NULL值等统计信息。

页面级别的zone_map是Doris存储引擎中的一种细粒度索引机制,其核心特点包括:

  1. 层级结构:每个数据页面都有一个对应的ZoneMapPB结构
  2. 存储格式:通过IndexedColumnMetaPB page_zone_maps字段存储,使用IndexedColumn结构
  3. 数据内容:包含每个页面的min、max、has_null、has_not_null等统计信息

从protobuf定义可以看出:

message ZoneMapIndexPB {// required: segment-level zone mapoptional ZoneMapPB segment_zone_map = 1;// required: zone map for each data page is stored in an IndexedColumn with ordinal indexoptional IndexedColumnMetaPB page_zone_maps = 2;
}

为什么(Why)

页面级别zone_map的设计目的:

  1. 精确过滤:相比段级别的zone_map,页面级别提供更细粒度的数据过滤能力
  2. 减少I/O:可以精确判断哪些页面需要读取,避免不必要的磁盘I/O
  3. 查询优化:结合ordinal index,可以快速定位到包含目标数据的具体页面

怎么做(How)

1. 构建过程分析

从代码分析可以看出,页面级别zone_map的构建流程:

在segment_writer.cpp中

// 创建列写入器时决定是否启用zone_map
opts.need_zone_map = column.is_key() || schema->keys_type() != KeysType::AGG_KEYS;

在zone_map_index.cpp中

  • TypedZoneMapIndexWriter::add_values():为每个传入的值更新当前页面的统计信息
  • TypedZoneMapIndexWriter::flush():页面完成时,将当前页面的zone_map写入IndexedColumn
  • TypedZoneMapIndexWriter::finish():完成所有页面zone_map的构建
2. 存储结构分析

页面级别zone_map使用IndexedColumn结构存储,这意味着:

  1. 有序存储:每个页面的zone_map按页面顺序存储
  2. 索引支持:通过ordinal index可以快速定位到特定页面的zone_map
  3. 压缩编码:支持各种编码和压缩算法
3. 与IndexedColumnWriter的关系

虽然indexed_column_writer.cpp本身不直接处理zone_map,但它提供了基础存储结构

  • IndexedColumnWriter负责构建有序的IndexedColumn
  • ZoneMapIndexWriter利用IndexedColumnWriter来存储页面级别的zone_map数据
  • 每个页面的zone_map作为单独的值添加到IndexedColumn中
4. 查询使用流程

查询时使用页面级别zone_map的流程:

  1. 段级别过滤:首先使用segment_zone_map进行粗粒度过滤
  2. 页面级别过滤:遍历page_zone_maps,找出可能包含目标数据的页面
  3. 精确读取:只读取通过过滤的页面,大幅减少I/O

总结

页面级别的zone_map是Doris存储引擎中细粒度索引的核心组件,它通过为每个数据页面维护独立的统计信息,实现了更精确的数据过滤和查询优化。其设计体现了空间换时间的经典思想,通过额外的存储开销换取显著的查询性能提升。

http://www.dtcms.com/a/454082.html

相关文章:

  • (1)SwiftUI基础入门教程
  • wordpress整站搬家教程河池做网站
  • 怎么改版一个网站白酒网站设计
  • windows显示驱动开发-节能与 VSync 控制
  • 安徽省住房和城乡建设厅官方网站网站访问量大
  • 好的平面设计网站有哪些易云巢做营销型网站
  • 从0死磕全栈之Next.js 中的错误处理机制详解(App Router)
  • c语言贪吃蛇游戏开发
  • 360网站seo优化怎么做简单网站
  • 小说网站静态模板设计师设计费一般多少
  • 网站主机要怎么做跨境电商交3980元培训费可信吗
  • Coze源码分析-资源库-编辑知识库-后端源码-流程/技术/总结
  • 哺乳动物双等位基因表达的 “守护者”--解析 MSL2对基因剂量平衡与疾病机制的新启示--文献精读164
  • 阿里云网站建设需要多少钱开发软件公司都有哪些
  • CTFHub SQL注入通关笔记4:布尔盲注(手注法+脚本法)
  • 虎书Fundamentals Of Computer Graphics翻译及笔记(1)——前言
  • 建网站公司室内设计效果图网站推荐
  • 网站建设收徒弟设计韩国电影完整版下载
  • 应聘网站优化的简历怎么做全新升级网站
  • 我的第一个网站
  • windows下将redis注册为windows服务
  • IT架构中的前后中台:SuperCell与阿里中台战略启示
  • 外贸工厂的网站建设长沙做网站智投未来
  • 国外商品网站企业微网站建站
  • 第六章 面向对象编程(基础部分)
  • 接口联调常见错误排查:从400 Bad Request到502 Gateway
  • 南通网站制作价格微金所网站谁做的
  • 域名哪个网站续费单项否决 网站建设
  • three.js与WebGL
  • 网站推广计划怎么写标书制作员是干什么的