bloom
提供了一种基于布隆过滤器的索引访问方法。
布隆过滤器是一种空间效率高的数据结构,用于测试元素是否是集合的成员。在索引访问方法的上下文中,它可以通过在索引创建时确定大小的签名快速排除不匹配的元组。
签名是有损的索引属性表示,因此容易报告误报;也就是说,它可能会报告一个元素在集合中,而实际上它不在。因此,索引搜索结果必须始终使用堆条目的实际属性值进行重新检查。较大的签名会减少误报的几率,从而减少无用的堆访问次数,但当然也会使索引更大,因此扫描速度更慢。
当一个表有许多属性并且查询测试它们的任意组合时,这种类型的索引最有用。传统的 btree 索引比 bloom 索引快,但它可能需要许多 btree 索引才能支持所有可能的查询,而只需要一个 bloom 索引。但请注意,bloom 索引只支持相等查询,而 btree 索引还可以执行不等式和范围搜索。
bloom
索引在其 WITH
子句中接受以下参数
length
每个签名(索引条目)的长度,以位为单位。它被向上舍入到最接近的 16
的倍数。默认值为 80
位,最大值为 4096
。
col1 — col32
为每个索引列生成的位数。每个参数的名称都指的是它控制的索引列的编号。默认值为 2
位,最大值为 4095
。实际上未使用的索引列的参数将被忽略。
这是一个创建布隆索引的示例
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 位。我们可以省略 length
、col1
和 col2
的规范,因为它们具有默认值。
这是一个更完整的布隆索引定义和使用示例,以及与等效 btree 索引的比较。布隆索引比 btree 索引小得多,并且可以表现得更好。
=# 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)
即使定义了 btree 索引,结果仍然是顺序扫描
=# 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)
在表上定义布隆索引比 btree 在处理这种类型的搜索时更好
=# 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)
现在,btree 搜索的主要问题是,当搜索条件不约束前导索引列时,btree 的效率很低。btree 的更好策略是在每个列上创建一个单独的索引。然后,计划器会选择如下内容
=# 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)
尽管此查询的运行速度比使用单个索引快得多,但我们在索引大小上付出了代价。每个单列 btree 索引占用 2 MB,因此所需的总空间为 12 MB,是布隆索引所用空间的八倍。
布隆索引的操作符类仅需要索引数据类型的哈希函数和一个用于搜索的相等运算符。此示例显示了 text
数据类型的操作符类定义
CREATE OPERATOR CLASS text_ops DEFAULT FOR TYPE text USING bloom AS OPERATOR 1 =(text, text), FUNCTION 1 hashtext(text);
该模块仅包含 int4
和 text
的操作符类。
搜索仅支持 =
运算符。但是将来有可能添加对具有并集和交集操作的数组的支持。
bloom
访问方法不支持 UNIQUE
索引。
bloom
访问方法不支持搜索 NULL
值。
Teodor Sigaev <[email protected]>
,Postgres Professional,俄罗斯莫斯科
Alexander Korotkov <[email protected]>
,Postgres Professional,俄罗斯莫斯科
Oleg Bartunov <[email protected]>
,Postgres Professional,俄罗斯莫斯科
如果您在文档中看到任何不正确的内容、与您使用特定功能的经验不符或需要进一步澄清的内容,请使用此表单报告文档问题。