BTree
Моя проблема тут полягає в тому, що індекс BTree буде величезним, оскільки afaict він буде зберігати повторювані значення (він також є, оскільки не може припустити, що таблиця фізично відсортована). Якщо BTree є величезним, мені в кінцевому підсумку доведеться читати і індекс, і частини таблиці, на які вказує і індекс ...
Не обов'язково - наявність індексу btree, який "охоплює", буде найшвидшим часом для читання, і якщо це все, що ви хочете (тобто, якщо ви можете дозволити собі додаткове сховище), то це ваша найкраща ставка.
БРІН
Я розумію, що я можу мати невеликий покажчик тут за рахунок читання непотрібних сторінок. Використання малого pages_per_range
означає, що індекс більший (що є проблемою з BRIN, оскільки мені потрібно прочитати весь індекс), маючи великий pages_per_range
засіб, що я прочитаю багато непотрібних сторінок.
Якщо ви не можете дозволити собі витрати на зберігання індексу покриття btree, BRIN ідеально підходить для вас, оскільки у вас вже є кластеризація (це важливо, щоб BRIN був корисним). Індекси BRIN невеликі , тому всі сторінки, ймовірно, будуть в пам'яті, якщо ви виберете відповідне значення pages_per_range
.
Чи є магічна формула, щоб знайти гарне значення pages_per_range, яке враховує ці компроміси?
Ніякої магічної формули, але почніть з pages_per_range
дещо меншого, ніж середній розмір (на сторінках), зайнятий середнім a
значенням. Ви, напевно, намагаєтеся мінімізувати: (кількість сканованих сторінок BRIN) + (кількість сканованих сторінок) для типового запиту. Шукайте Heap Blocks: lossy=n
у плані виконання pages_per_range=1
та порівняйте його з іншими значеннями pages_per_range
- тобто подивіться, скільки скануючих непотрібних блоків купи.
GIN / GiST
Не впевнені, що вони тут є актуальними, оскільки вони в основному використовуються для повного пошуку тексту, але я також чую, що вони добре справляються із дублюючими ключами. Чи допоможе тут GIN
/ a GiST
index?
GIN, можливо, варто розглянути, але, мабуть, не GiST - однак, якщо природне кластеринг дійсно хороший, то BRIN, ймовірно, буде кращим.
Ось зразкове порівняння між різними типами індексу для фіктивних даних, схоже на ваше:
таблиця та індекси:
create table foo(a,b,c) as
select *, lpad('',20)
from (select chr(g) a from generate_series(97,122) g) a
cross join (select generate_series(1,100000) b) b
order by a;
create index foo_btree_covering on foo(a,b);
create index foo_btree on foo(a);
create index foo_gin on foo using gin(a);
create index foo_brin_2 on foo using brin(a) with (pages_per_range=2);
create index foo_brin_4 on foo using brin(a) with (pages_per_range=4);
vacuum analyze;
відношення розмірів:
select relname "name", pg_size_pretty(siz) "size", siz/8192 pages, (select count(*) from foo)*8192/siz "rows/page"
from( select relname, pg_relation_size(C.oid) siz
from pg_class c join pg_namespace n on n.oid = c.relnamespace
where nspname = current_schema ) z;
назва | розмір | сторінки | рядки / сторінки
: ----------------- | : ------ | ----: | --------:
foo | 149 МБ | 19118 | 135
foo_btree_covering | 56 МБ | 7132 | 364
foo_btree | 56 МБ | 7132 | 364
foo_gin | 2928 кБ | 366 | 7103
foo_brin_2 | 264 кБ | 33 | 78787
foo_brin_4 | 136 кБ | 17 | 152941
покриття btree:
explain analyze select sum(b) from foo where a='a';
| ПИТАННЯ ПЛАНУ |
| : ------------------------------------------------- -------------------------------------------------- ------------------------------------------- |
| Сукупність (вартість = 3282,57..3282,58 рядків = 1 ширина = 8) (фактичний час = 45.942..45.942 рядки = 1 петля = 1) |
| -> Сканувати лише з індексом за допомогою foo_btree_covering на foo (вартість = 0,43..3017,80 рядків = 105907 ширина = 4) (фактичний час = 0,038..27,286 рядків = 100000 циклів = 1) |
| Індекс Cond: (a = 'a' :: текст) |
| Купа: 0 |
| Час планування: 0.099 мс |
| Час виконання: 45,968 мс |
звичайний btree:
drop index foo_btree_covering;
explain analyze select sum(b) from foo where a='a';
| ПИТАННЯ ПЛАНУ |
| : ------------------------------------------------- -------------------------------------------------- ----------------------------- |
| Сукупна (вартість = 4064.57..4064.58 рядків = 1 ширина = 8) (фактичний час = 54.242..54.242 рядки = 1 петля = 1) |
| -> Сканування покажчика за допомогою foo_btree on foo (вартість = 0,43..3799,80 рядків = 105907 ширина = 4) (фактичний час = 0,037..33,048 рядків = 100000 петель = 1) |
| Індекс Cond: (a = 'a' :: текст) |
| Час планування: 0,135 мс |
| Час виконання: 54.280 мс |
BRIN сторінки_per_range = 4:
drop index foo_btree;
explain analyze select sum(b) from foo where a='a';
| ПИТАННЯ ПЛАНУ |
| : ------------------------------------------------- -------------------------------------------------- ----------------------------- |
| Сукупна (вартість = 21595.38..21595.39 рядів = 1 ширина = 8) (фактичний час = 52.455..52.455 рядів = 1 петля = 1) |
| -> Bitmap Heap Scan on foo (вартість = 888,78..21330,61 рядків = 105907 ширина = 4) (фактичний час = 2,738..31,967 рядків = 100000 петель = 1) |
| Перевірте умову: (a = 'a' :: текст) |
| Рядки видалені за допомогою повторної перевірки індексу: 96 |
| Купи блоків: втрата = 736 |
| -> Сканування індексу Bitmap на foo_brin_4 (вартість = 0,00..862,30 рядків = 105907 ширина = 0) (фактичний час = 2,720..2,720 рядків = 7360 циклів = 1) |
| Індекс Cond: (a = 'a' :: текст) |
| Час планування: 0,101 мс |
| Час виконання: 52.501 мс |
BRIN сторінки_per_range = 2:
drop index foo_brin_4;
explain analyze select sum(b) from foo where a='a';
| ПИТАННЯ ПЛАНУ |
| : ------------------------------------------------- -------------------------------------------------- ----------------------------- |
| Сукупна (вартість = 21659.38..21659.39 рядів = 1 ширина = 8) (фактичний час = 53.971..53.971 ряди = 1 петля = 1) |
| -> Bitmap Heap Scan on foo (вартість = 952,78..21394,61 рядків = 105907 ширина = 4) (фактичний час = 5.286..33.492 рядки = 100000 петель = 1) |
| Перевірте умову: (a = 'a' :: текст) |
| Рядки видалені за допомогою повторної перевірки індексу: 96 |
| Купи блоків: втрата = 736 |
| -> Сканування індексу растрових зображень на foo_brin_2 (вартість = 0,00..926,30 рядків = 105907 ширина = 0) (фактичний час = 5,275..5,275 рядків = 7360 циклів = 1) |
| Індекс Cond: (a = 'a' :: текст) |
| Час планування: 0,095 мс |
| Час виконання: 54.016 мс |
GIN:
drop index foo_brin_2;
explain analyze select sum(b) from foo where a='a';
| ПИТАННЯ ПЛАНУ |
| : ------------------------------------------------- -------------------------------------------------- ------------------------------ |
| Сукупна (вартість = 21687.38..21687.39 рядків = 1 ширина = 8) (фактичний час = 55.331..55.331 рядів = 1 петля = 1) |
| -> Bitmap Heap Scan on foo (вартість = 980,78..21422,61 рядків = 105907 ширина = 4) (фактичний час = 12,377..33,956 рядків = 100000 петель = 1) |
| Перевірте умову: (a = 'a' :: текст) |
| Купи блоків: точні = 736 |
| -> Сканування індексу растрових зображень на foo_gin (вартість = 0,00..954,30 рядків = 105907 ширина = 0) (фактичний час = 12,271..12,271 рядків = 100000 петель = 1) |
| Індекс Cond: (a = 'a' :: текст) |
| Час планування: 0,118 мс |
| Час виконання: 55.366 мс |
dbfiddle тут