bloom
提供了一個基於 布隆過濾器 的索引訪問方法。
布隆過濾器是一種節省空間的資料結構,用於測試一個元素是否屬於一個集合。在索引訪問方法的場景下,它允許透過在索引建立時確定大小的簽名(signatures)來快速排除不匹配的行(tuples)。
簽名是對被索引列(或列組合)的一種有損表示,因此容易報告假陽性(false positives);也就是說,它可能報告一個元素存在於集合中,而實際上並不存在。因此,索引搜尋結果必須始終使用堆(heap)條目中的實際列值進行重新檢查。更大的簽名可以降低假陽性的機率,從而減少無用的堆訪問次數,但當然也會使索引更大,掃描起來更慢。
當一個表有很多列,並且查詢測試這些列的任意組合時,這種型別的索引最為有用。傳統的 btree 索引比 bloom 索引快,但可能需要很多 btree 索引才能支援所有可能的查詢,而一個 bloom 索引就足夠了。但請注意,bloom 索引僅支援等值查詢,而 btree 索引還可以執行不等值和範圍搜尋。
一個 bloom
索引在其 WITH
子句中接受以下引數:
length
每個簽名(索引條目)的長度,以位(bits)為單位。它會向上舍入到最近的 16
的倍數。預設值為 80
位,最大值為 4096
位。
col1 — col32
為每個索引列生成的位數。每個引數的名稱指的是它所控制的索引列的編號。預設值為 2
位,最大值為 4095
位。對於未實際使用的索引列的引數將被忽略。
這是一個建立 bloom 索引的示例:
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
的值是預設值,因此可以省略它們的說明。
以下是一個更完整的 bloom 索引定義和使用示例,以及與等效 btree 索引的比較。bloom 索引比 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..213744.00 rows=250 width=24) (actual time=357.059..357.059 rows=0.00 loops=1) Filter: ((i2 = 898732) AND (i5 = 123451)) Rows Removed by Filter: 10000000 Buffers: shared hit=63744 Planning Time: 0.346 ms Execution Time: 357.076 ms (6 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 ---------------- 386 MB (1 row) =# EXPLAIN ANALYZE SELECT * FROM tbloom WHERE i2 = 898732 AND i5 = 123451; QUERY PLAN ------------------------------------------------------------------------------------------------------ Seq Scan on tbloom (cost=0.00..213744.00 rows=2 width=24) (actual time=351.016..351.017 rows=0.00 loops=1) Filter: ((i2 = 898732) AND (i5 = 123451)) Rows Removed by Filter: 10000000 Buffers: shared hit=63744 Planning Time: 0.138 ms Execution Time: 351.035 ms (6 rows)
在表上定義 bloom 索引比 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 ---------------- 153 MB (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=22.605..22.606 rows=0.00 loops=1) Recheck Cond: ((i2 = 898732) AND (i5 = 123451)) Rows Removed by Index Recheck: 2300 Heap Blocks: exact=2256 Buffers: shared hit=21864 -> Bitmap Index Scan on bloomidx (cost=0.00..178436.00 rows=1 width=0) (actual time=20.005..20.005 rows=2300.00 loops=1) Index Cond: ((i2 = 898732) AND (i5 = 123451)) Index Searches: 1 Buffers: shared hit=19608 Planning Time: 0.099 ms Execution Time: 22.632 ms (11 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=9.29..13.30 rows=1 width=24) (actual time=0.032..0.033 rows=0.00 loops=1) Recheck Cond: ((i5 = 123451) AND (i2 = 898732)) Buffers: shared read=6 -> BitmapAnd (cost=9.29..9.29 rows=1 width=0) (actual time=0.047..0.047 rows=0.00 loops=1) Buffers: shared hit=6 -> Bitmap Index Scan on btreeidx5 (cost=0.00..4.52 rows=11 width=0) (actual time=0.026..0.026 rows=7.00 loops=1) Index Cond: (i5 = 123451) Index Searches: 1 Buffers: shared hit=3 -> Bitmap Index Scan on btreeidx2 (cost=0.00..4.52 rows=11 width=0) (actual time=0.007..0.007 rows=8.00 loops=1) Index Cond: (i2 = 898732) Index Searches: 1 Buffers: shared hit=3 Planning Time: 0.264 ms Execution Time: 0.047 ms (15 rows)
儘管這個查詢比使用任何單個索引都執行得快得多,但我們在索引大小上付出了代價。每個單列 btree 索引佔用 88.5 MB,因此總空間需求為 531 MB,是 bloom 索引所用空間的三倍多。
bloom 索引的運算子類僅需要索引資料型別的雜湊函式和搜尋的相等性運算子。本示例展示了 text
資料型別的運算子類定義:
CREATE OPERATOR CLASS text_ops DEFAULT FOR TYPE text USING bloom AS OPERATOR 1 =(text, text), FUNCTION 1 hashtext(text);
該模組僅包含 int4
和 text
資料型別的運算子類。
僅支援 =
運算子進行搜尋。但未來有可能為陣列新增支援,支援聯合(union)和交集(intersection)操作。
bloom
訪問方法不支援 UNIQUE
索引。
bloom
訪問方法不支援搜尋 NULL
值。
Teodor Sigaev <teodor@postgrespro.ru>
,Postgres Professional,莫斯科,俄羅斯
Alexander Korotkov <a.korotkov@postgrespro.ru>
,Postgres Professional,莫斯科,俄羅斯
Oleg Bartunov <obartunov@postgrespro.ru>
,Postgres Professional,莫斯科,俄羅斯
如果您在文件中看到任何不正確、與您對特定功能的經驗不符或需要進一步澄清的內容,請使用 此表格 報告文件問題。