跳到主要内容
版本:2.x

BRIN 索引

当表非常大,且某些列的值与其物理存储位置存在天然关联时,可以使用块范围索引(BRIN)。BRIN 按块范围(或页面范围)进行索引,这些范围是表中物理上相邻的一组页面。BRIN 为每个块范围存储摘要信息。例如,一个存储订单数据的表中,订单日期较早的记录往往靠前;又如,存储邮政编码的表中,相同城市的编码可能被自然聚集在一起。

BRIN 索引可通过位图索引扫描参与查询。当索引中存储的摘要信息与查询条件匹配时,查询会访问该范围内的所有页面,并返回其中的所有元组。查询执行器会逐一验证这些元组,仅保留满足条件的结果,因此 BRIN 是一种“有损”索引。由于 BRIN 索引本身非常小,因此与顺序扫描相比,其额外开销很低,同时能跳过大量不相关的数据块。

BRIN 索引存储哪些数据、能加速哪些查询,取决于为每列选择的操作符类。对于具有线性排序的列,索引会记录每个块范围内的最小值和最大值;而几何类型则记录块范围内所有对象的边界框。

块范围的大小由创建索引时的 pages_per_range 参数决定。索引中条目的数量等于表的页面总数除以 pages_per_range 值。该值越小,索引越大(因为条目更多),但摘要信息也越精细,扫描时可跳过更多不相关的数据块。pages_per_range 默认值为:堆表为 32,追加优化表为 1

提示

从 v2.0.0 起,BRIN 索引支持在 AO 表和 CO 表上创建,并通过链式 revmap 结构优化存储,降低空间占用。该优化提升了存储结构与摘要维护的效率,同时支持通过 brin_summarize_range() 手动生成摘要。

虽然在 AO/CO 表上可以创建并维护 BRIN 索引,但是否会被优化器使用,还取决于查询条件、数据分布、页面数量以及启发式成本模型。因此,并非所有使用 BRIN 的场景都能自动获得性能收益,建议在数据量大、数据具有良好聚簇性的场景中使用。

维护 BRIN 索引

在索引创建时,Apache Cloudberry 会扫描已有堆页面,并为每个范围生成摘要索引元组,包括可能不完整的尾部范围。当数据写入填满新页面时,系统会尝试更新已计算范围的摘要信息。但对于新页面所在的范围,如果其尚未被计算,系统不会自动创建摘要元组。这些范围会处于“未计算”状态,需通过手动方式触发摘要生成。

以下方式可用于生成初始摘要:

  • 执行 VACUUM 操作时,系统会自动计算所有尚未处理的页面范围。

也可以使用以下函数手动控制:

  • brin_summarize_new_values(regclass):计算所有尚未处理的范围;
  • brin_summarize_range(regclass, bigint):只计算包含指定页面的范围(若尚未计算)。

相反,如需撤销摘要信息,可使用 brin_desummarize_range(regclass, bigint) 函数取消已计算的范围。当数据发生变更,原有摘要信息不再准确时,该操作尤为有用。

下表列出了可用于 BRIN 索引维护的函数。它们不能在恢复模式下执行,仅限超级用户或索引所有者调用。

名称返回类型描述
brin_summarize_new_values(index regclass)integer计算所有未被处理的页面范围。
brin_summarize_range(index regclass, blockNumber bigint)integer若指定块所在范围未被计算,则对其生成摘要。
brin_desummarize_range(index regclass, blockNumber bigint)integer若指定块所在范围已被计算,则撤销其摘要信息。

brin_summarize_new_values 接受索引名称或 OID,自动查找尚未被计算的范围,并扫描对应表页面生成新的摘要索引元组。它返回生成的摘要条目数量。brin_summarize_range 则只处理包含指定块号的那一个范围。

提示
  • 通常情况下,在执行 VACUUM 之后,brin_summarize_new_values() 不会产生新的摘要,因为默认行为是在清理过程中自动处理所有新增页面范围。该函数主要用于手动摘要刷新,适用于未开启自动清理或写入频繁的场景。
  • 若表的页数较少或查询条件选择性不足,优化器可能仍不会选用 BRIN 索引路径。
  • brin_summarize_rangebrin_desummarize_range 仅对实际存在的范围生效。若提供了不存在的块号,函数会返回 0。由于数据分布不均,某些范围可能只存在于部分 Segment 上,函数将返回成功处理该范围的 Segment 数量。