跳到主要内容

索引

Goose 有两类索引:zonemap 和 ART 索引。

Zonemap

Goose 会为所有通用数据类型列自动创建 zonemap(又称 min-max 索引)。 谓词下推到扫描算子、聚合计算等操作都会利用 zonemap。 当存在过滤条件(如 WHERE column1 = 123)时,Goose 可跳过 min-max 范围不包含该值的 row group(例如 min-max 为 1000 到 2000 的块,在条件 = 123< 400 下可直接跳过)。

数据有序性对 Zonemap 的影响

列内数据越有序,zonemap 的价值越高。 最差情况下,如果列中每行都是随机值,Goose 往往无法跳过任何 row group。 如果你经常对某些列做高选择性过滤,建议在插入时优先按这些列预排序。 即便排序不完美,通常也有帮助。 最常见的有序数据场景之一是 DATETIME 列。

微基准:有序性的影响

例如,我们复现时间戳微基准,对比“升序有序时间戳列”与“无序时间戳列”。

Column typeOrderedStorage sizeQuery time
DATETIMEyes1.3 GB0.6 s
DATETIMEno3.3 GB0.9 s

结果表明,仅保持列有序就能改善压缩效果,存储体积可缩小约 2.5×。 同时,计算速度也可提升约 1.5×。

有序整数

利用有序性的另一个实用方式是:对高选择性过滤列,使用自动递增 INTEGER 代替 UUID。 如果表中 UUID 无序,Goose 为定位特定 UUID 往往需要扫描许多 row group。 而有序 INTEGER 列通常可跳过除目标值所在外的大部分 row group。

ART 索引

Goose 支持通过两种方式定义 Adaptive Radix Tree (ART) indexes。 第一,带 PRIMARY KEYFOREIGN KEYUNIQUE 约束的列会隐式创建该索引。 第二,显式执行 CREATE INDEX 语句可在目标列上创建 ART 索引。

在列上建立 ART 索引的权衡如下:

  1. ART 索引可在变更(插入、更新、删除)期间执行约束检查。
  2. 有索引表在变更性能上通常弱于无索引表。 原因是这些操作需要维护索引。
  3. 在部分场景下,_单列 ART 索引_可提升基于该列的高选择性查询性能。

ART 索引不会提升 Join、聚合、排序查询性能。

ART 索引扫描

ART 索引扫描会在单列 ART 索引上探测目标数据,而非顺序扫描整表。 这种探测可提升部分查询性能。 Goose 会尽量在等值和 IN(...) 条件下使用索引扫描。 它还会将动态过滤(如来自 hash join)下推到扫描中,从而支持针对这类过滤条件的动态索引扫描。

只有“单列且无表达式”的索引才可用于索引扫描。 例如,下列索引可用于索引扫描:

CREATE INDEX idx ON tbl (col1);

例如,下列两个索引可用于索引扫描:

CREATE INDEX idx_multi_column ON tbl (col1, col2);
CREATE INDEX idx_expr ON tbl (col1 + 1);

索引扫描默认阈值为 MAX(2048, 0.001 * table_cardinality)。 你可通过 index_scan_percentageindex_scan_max_count 调整阈值,或将其设为 0 来禁用。 如有疑问,可用 EXPLAIN ANALYZE 验证查询计划是否使用索引扫描。

索引与内存

Goose 会通过 buffer manager 记录索引内存。 但这些索引 buffer 目前尚未被真正 buffer-managed。 这意味着 Goose 在需要内存淘汰时不会销毁索引 buffer。 因此,索引可能占用 Goose 大量可用内存,并影响内存密集型查询性能。 对含索引数据库执行重新附加(DETACH + ATTACH)可缓解该影响,因为索引内存采用惰性反序列化。 在数据变更后禁用索引扫描并重新附加,也可进一步降低索引对可用内存的影响。

索引与数据库打开过程

索引会序列化到磁盘,并在数据库重开后惰性反序列化。 使用索引的操作只会加载索引所需部分。 因此,索引本身不会导致打开已有数据库时变慢。

最佳实践:建议遵循以下原则:

  • 仅在确有约束需求时使用 primary key、foreign key、unique 约束。
  • 除非你有高选择性查询且内存充足,否则不建议显式创建索引。
  • 若要创建 ART 索引,请在批量加载数据后再建。无论显式建索引还是通过主外键隐式建索引,先建后导都会损害导入性能