2024年9月26日:PostgreSQL 17 发布!
支持的版本:当前 (17) / 16 / 15 / 14 / 13 / 12
开发版本:devel
不支持的版本:11 / 10 / 9.6

F.6. bloom — 布隆过滤器索引访问方法 #

bloom 提供了一种基于布隆过滤器的索引访问方法。

布隆过滤器是一种节省空间的数据结构,用于测试元素是否为集合的成员。在索引访问方法的情况下,它允许通过在索引创建时确定大小的签名快速排除不匹配的元组。

签名是索引属性的损失表示,因此容易报告误报;也就是说,它可能会报告一个元素在集合中,而实际上不在。因此,索引搜索结果必须始终使用堆条目中的实际属性值重新检查。更大的签名减少了误报的可能性,从而减少了无用的堆访问次数,但当然也使索引更大,因此扫描速度更慢。

当表具有许多属性并且查询测试它们的任意组合时,这种类型的索引最有用。传统的 B 树索引比布隆索引快,但它可能需要许多 B 树索引来支持所有可能的查询,而只需一个布隆索引即可。但是请注意,布隆索引仅支持等值查询,而 B 树索引还可以执行不等式和范围搜索。

F.6.1. 参数 #

bloom 索引在其 WITH 子句中接受以下参数

length

每个签名(索引条目)的长度(以位为单位)。它会向上舍入到最接近的 16 的倍数。默认值为 80 位,最大值为 4096

col1 — col32

为每个索引列生成的位数。每个参数的名称指的是它控制的索引列的编号。默认值为 2 位,最大值为 4095。实际上未使用索引列的参数将被忽略。

F.6.2. 示例 #

这是一个创建布隆索引的示例

CREATE INDEX bloomidx ON tbloom USING bloom (i1,i2,i3)
       WITH (length=80, col1=2, col2=2, col3=4);

该索引是用 80 位的签名长度创建的,其中属性 i1 和 i2 映射到 2 位,属性 i3 映射到 4 位。我们可以省略 lengthcol1col2 规范,因为它们具有默认值。

这是一个更完整的布隆索引定义和用法的示例,以及与等效的 B 树索引的比较。布隆索引比 B 树索引小得多,并且可以执行得更好。

=# CREATE TABLE tbloom AS
   SELECT
     (random() * 1000000)::int as i1,
     (random() * 1000000)::int as i2,
     (random() * 1000000)::int as i3,
     (random() * 1000000)::int as i4,
     (random() * 1000000)::int as i5,
     (random() * 1000000)::int as i6
   FROM
  generate_series(1,10000000);
SELECT 10000000

对这个大表进行顺序扫描需要很长时间

=# EXPLAIN ANALYZE SELECT * FROM tbloom WHERE i2 = 898732 AND i5 = 123451;
                                              QUERY PLAN
-------------------------------------------------------------------​-----------------------------------
 Seq Scan on tbloom  (cost=0.00..2137.14 rows=3 width=24) (actual time=16.971..16.971 rows=0 loops=1)
   Filter: ((i2 = 898732) AND (i5 = 123451))
   Rows Removed by Filter: 100000
 Planning Time: 0.346 ms
 Execution Time: 16.988 ms
(5 rows)

即使定义了 B 树索引,结果仍然是顺序扫描

=# CREATE INDEX btreeidx ON tbloom (i1, i2, i3, i4, i5, i6);
CREATE INDEX
=# SELECT pg_size_pretty(pg_relation_size('btreeidx'));
 pg_size_pretty
----------------
 3976 kB
(1 row)
=# EXPLAIN ANALYZE SELECT * FROM tbloom WHERE i2 = 898732 AND i5 = 123451;
                                              QUERY PLAN
-------------------------------------------------------------------​-----------------------------------
 Seq Scan on tbloom  (cost=0.00..2137.00 rows=2 width=24) (actual time=12.805..12.805 rows=0 loops=1)
   Filter: ((i2 = 898732) AND (i5 = 123451))
   Rows Removed by Filter: 100000
 Planning Time: 0.138 ms
 Execution Time: 12.817 ms
(5 rows)

在表上定义布隆索引比 B 树在处理此类搜索方面更好

=# CREATE INDEX bloomidx ON tbloom USING bloom (i1, i2, i3, i4, i5, i6);
CREATE INDEX
=# SELECT pg_size_pretty(pg_relation_size('bloomidx'));
 pg_size_pretty
----------------
 1584 kB
(1 row)
=# EXPLAIN ANALYZE SELECT * FROM tbloom WHERE i2 = 898732 AND i5 = 123451;
                                                     QUERY PLAN
-------------------------------------------------------------------​--------------------------------------------------
 Bitmap Heap Scan on tbloom  (cost=1792.00..1799.69 rows=2 width=24) (actual time=0.388..0.388 rows=0 loops=1)
   Recheck Cond: ((i2 = 898732) AND (i5 = 123451))
   Rows Removed by Index Recheck: 29
   Heap Blocks: exact=28
   ->  Bitmap Index Scan on bloomidx  (cost=0.00..1792.00 rows=2 width=0) (actual time=0.356..0.356 rows=29 loops=1)
         Index Cond: ((i2 = 898732) AND (i5 = 123451))
 Planning Time: 0.099 ms
 Execution Time: 0.408 ms
(8 rows)

现在,B 树搜索的主要问题是当搜索条件不限制前导索引列时,B 树效率低下。B 树的一个更好的策略是在每一列上创建一个单独的索引。然后规划器将选择类似这样的内容

=# CREATE INDEX btreeidx1 ON tbloom (i1);
CREATE INDEX
=# CREATE INDEX btreeidx2 ON tbloom (i2);
CREATE INDEX
=# CREATE INDEX btreeidx3 ON tbloom (i3);
CREATE INDEX
=# CREATE INDEX btreeidx4 ON tbloom (i4);
CREATE INDEX
=# CREATE INDEX btreeidx5 ON tbloom (i5);
CREATE INDEX
=# CREATE INDEX btreeidx6 ON tbloom (i6);
CREATE INDEX
=# EXPLAIN ANALYZE SELECT * FROM tbloom WHERE i2 = 898732 AND i5 = 123451;
                                                        QUERY PLAN
-------------------------------------------------------------------​--------------------------------------------------------
 Bitmap Heap Scan on tbloom  (cost=24.34..32.03 rows=2 width=24) (actual time=0.028..0.029 rows=0 loops=1)
   Recheck Cond: ((i5 = 123451) AND (i2 = 898732))
   ->  BitmapAnd  (cost=24.34..24.34 rows=2 width=0) (actual time=0.027..0.027 rows=0 loops=1)
         ->  Bitmap Index Scan on btreeidx5  (cost=0.00..12.04 rows=500 width=0) (actual time=0.026..0.026 rows=0 loops=1)
               Index Cond: (i5 = 123451)
         ->  Bitmap Index Scan on btreeidx2  (cost=0.00..12.04 rows=500 width=0) (never executed)
               Index Cond: (i2 = 898732)
 Planning Time: 0.491 ms
 Execution Time: 0.055 ms
(9 rows)

尽管此查询比使用任何一个单列索引都要快得多,但我们在索引大小方面付出了代价。每个单列 B 树索引占用 2 MB,因此所需的总空间为 12 MB,是布隆索引使用空间的八倍。

F.6.3. 运算符类接口 #

布隆索引的运算符类只需要索引数据类型的哈希函数和用于搜索的等值运算符。此示例显示了 text 数据类型的运算符类定义

CREATE OPERATOR CLASS text_ops
DEFAULT FOR TYPE text USING bloom AS
    OPERATOR    1   =(text, text),
    FUNCTION    1   hashtext(text);

F.6.4. 限制 #

  • 模块中仅包含 int4text 的运算符类。

  • 仅支持 = 运算符进行搜索。但将来有可能添加对使用联合和交集运算的数组的支持。

  • bloom 访问方法不支持 UNIQUE 索引。

  • bloom 访问方法不支持搜索 NULL 值。

F.6.5. 作者 #

Teodor Sigaev ,Postgres Professional,俄罗斯莫斯科

Alexander Korotkov ,Postgres Professional,俄罗斯莫斯科

Oleg Bartunov ,Postgres Professional,俄罗斯莫斯科

提交更正

如果您在文档中看到任何不正确的内容,与您对特定功能的体验不符,或者需要进一步说明,请使用此表单 报告文档问题。