F.7. bloom#
F.7. bloom
bloom
предоставляет метод доступа к индексу, основанный на
фильтрах Блума.
Bloom-фильтр - это эффективная по использованию пространства структура данных, которая используется для проверки, является ли элемент членом множества. В случае доступа к индексу, он позволяет быстро исключать несоответствующие кортежи с помощью подписей, размер которых определяется при создании индекса.
Подпись - это потерянное представление индексируемого атрибута(ов) и, следовательно, подвержено ложным срабатываниям; то есть может быть сообщено, что элемент находится в наборе, когда это не так. Поэтому результаты поиска по индексу всегда должны быть проверены с использованием фактических значений атрибутов из записи кучи. Более крупные подписи уменьшают вероятность ложного срабатывания и, следовательно, уменьшают количество бесполезных посещений кучи, но, конечно же, делают индекс больше и, следовательно, медленнее для сканирования.
Этот тип индекса наиболее полезен, когда у таблицы много атрибутов и запросы проверяют произвольные комбинации из них. Традиционный индекс btree работает быстрее, чем индекс bloom, но может потребоваться много индексов btree для поддержки всех возможных запросов, где нужен только один индекс bloom. Однако следует отметить, что индексы bloom поддерживают только запросы на равенство, в то время как индексы btree также могут выполнять поиск неравенства и диапазона.
F.7.1. Параметры
A индекс bloom
принимает следующие параметры в своем предложении
WITH
:
length
Длина каждой подписи (индексной записи) в битах. Она округляется до ближайшего кратного числа
16
. По умолчанию используется80
бит, а максимальное значение составляет4096
бит.
col1 — col32
Количество бит, генерируемых для каждого столбца индекса. Имя каждого параметра относится к номеру управляемого им столбца индекса. По умолчанию используется
2
бита, а максимальное значение -4095
. Параметры для неиспользуемых столбцов индекса игнорируются.
F.7.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 бита. Мы могли бы опустить спецификации length
, col1
и col2
, так как они имеют значения по умолчанию.
Вот более полный пример определения и использования индекса Блюма, а также сравнение с эквивалентными индексами 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)
Даже при наличии определенного индекса 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 МБ, поэтому общий объем необходимого места составляет 12 МБ, восемь раз больше, чем место, занимаемое индексом bloom.
F.7.3. Интерфейс класса операторов
Класс операторов для индексов Bloom требует только хеш-функцию для типа данных, по которому создается индекс, и оператор равенства для поиска. В этом примере показано определение класса операторов для типа данных text
:
CREATE OPERATOR CLASS text_ops DEFAULT FOR TYPE text USING bloom AS OPERATOR 1 =(text, text), FUNCTION 1 hashtext(text);
F.7.4. Ограничения
Только классы операторов для типов
int4
иtext
включены в модуль.Только оператор
=
поддерживается для поиска. Однако в будущем возможно добавление поддержки массивов с операциями объединения и пересечения.Метод доступа
bloom
не поддерживаетUNIQUE
индексы.Метод доступа
bloom
не поддерживает поиск значенийNULL
.
F.7.5. Авторы
Федор Сигаев <teodor@postgrespro.ru>
,
Postgres Professional, Москва, Россия
Александр Коротков <a.korotkov@postgrespro.ru>
, Postgres Professional, Москва, Россия.
Олег Бартунов <obartunov@postgrespro.ru>
,
Postgres Professional, Москва, Россия