BRIN (Block Range INdex) зберігає не кожне значення, а підсумок для діапазону сторінок таблиці: мінімум і максимум значення колонки в кожних, скажімо, 128 сторінках.
Запит WHERE created_at >= '2026-10-01' перевіряє підсумки й читає лише ті діапазони сторінок, де значення можуть бути, - решту пропускає.
CREATE INDEX events_created_brin ON events USING brin (created_at);
CREATE INDEX events_created_brin ON events USING brin (created_at) WITH (pages_per_range = 32);
Головна перевага - розмір. На таблиці в сотні гігабайтів B-tree займає десятки гігабайтів, а BRIN - мегабайти. Він майже не впливає на швидкість вставки.
Коли BRIN працює: значення корелюють з фізичним розташуванням рядків на диску. Ідеальний випадок - журнали, події, метрики, де рядки лише дописуються в кінець, а час вставки зростає. Тоді кожен діапазон сторінок містить вузький проміжок часу.
Коли не працює:
- Значення розкидані по таблиці випадково (наприклад,
user_idу журналі подій) - кожен діапазон містить майже всі значення, і BRIN нічого не відсікає. - Часті
UPDATEчи видалення з подальшими вставками на звільнене місце руйнують кореляцію. - Потрібен точковий пошук одного рядка - BRIN поверне діапазони сторінок, які доведеться перечитати.
Як перевірити придатність: correlation у pg_stats для колонки - близько до 1 чи -1 означає, що BRIN підійде.
Обслуговування: нові сторінки підсумовуються вакуумом або автоматично (autosummarize = on), або вручну brin_summarize_new_values(). Поки діапазон не підсумовано, його доводиться читати завжди.
Типова архітектура: BRIN за часом на великій таблиці подій + партиціонування за місяцями + B-tree лише на тих колонках, за якими справді шукають окремі записи.