Давайте сделаем несколько предположений:
У меня есть таблица, которая выглядит так:
a | b
---+---
a | -1
a | 17
...
a | 21
c | 17
c | -3
...
c | 22
Факты о моем наборе:
Размер всей таблицы ~ 10 10 строк.
У меня есть ~ 100 000 строк со значением
a
в столбцеa
, аналогично для других значений (напримерc
).Это означает ~ 100 тыс. Различных значений в столбце «а».
Большинство моих запросов будут читать все или большинство значений для данного значения в, например
select sum(b) from t where a = 'c'
.Таблица написана таким образом, что последовательные значения физически близки (либо она написана по порядку, либо мы предполагаем, что
CLUSTER
она использовалась для этой таблицы и столбцаa
).Таблица редко обновляется, если вообще обновляется, нас интересует только скорость чтения.
Таблица является относительно узкой (скажем, ~ 25 байт на кортеж, + 23 байта служебных данных).
Теперь вопрос в том, какой индекс мне использовать? Мое понимание таково:
BTree Моя проблема здесь в том, что индекс BTree будет огромным, поскольку, насколько я знаю, он будет хранить повторяющиеся значения (это необходимо, поскольку он не может предполагать, что таблица физически отсортирована). Если BTree огромен, мне приходится читать как индекс, так и части таблицы, на которые указывает индекс. (Мы можем использовать,
fillfactor = 100
чтобы немного уменьшить размер индекса.)Брин Я понимаю, что я могу иметь небольшой индекс за счет чтения бесполезных страниц. Использование маленького
pages_per_range
означает, что индекс больше (что является проблемой для BRIN, так как мне нужно прочитать весь индекс), имея большое значениеpages_per_range
, я буду читать много бесполезных страниц. Есть ли волшебная формула, чтобы найти хорошее значение,pages_per_range
которое учитывает эти компромиссы?GIN / GiST Не уверен, что они здесь уместны, поскольку они в основном используются для полнотекстового поиска, но я также слышал, что они хорошо справляются с дублирующимися ключами. Поможет ли здесь
GIN
илиGiST
индекс?
Другой вопрос заключается в том, будет ли Postgres использовать тот факт, что таблица CLUSTER
редактируется (при условии отсутствия обновлений) в планировщике запросов (например, путем двоичного поиска соответствующих начальных / конечных страниц)? Что-то связанное, могу ли я просто сохранить все свои столбцы в BTree и полностью удалить таблицу (или достичь чего-то эквивалентного, я считаю, что это кластерные индексы в SQL-сервере)? Есть ли какой-нибудь гибридный индекс BTree / BRIN, который бы здесь помог?
Я бы предпочел не использовать массивы для хранения моих значений, поскольку мой запрос в результате будет менее читаемым (я понимаю, что это снизит затраты на 23 байта на заголовок кортежа за счет уменьшения количества кортежей).
Ответы:
Не обязательно - наличие «покрывающего» индекса btree будет самым быстрым временем чтения, а если это все, что вы хотите (т.е. если вы можете позволить себе дополнительное хранилище), то это ваша лучшая ставка.
Если вы не можете позволить себе нехватку хранилища для покрывающего индекса btree, BRIN идеально подходит для вас, потому что у вас уже есть кластеризация (это важно для BRIN, чтобы быть полезным). Индексы BRIN крошечные , поэтому все страницы, вероятно, будут в памяти, если вы выберете подходящее значение
pages_per_range
.Нет волшебной формулы, но начнем с
pages_per_range
несколько меньшего, чем средний размер (в страницах), занятый среднимa
значением. Возможно, вы пытаетесь свести к минимуму: (количество отсканированных страниц BRIN) + (количество отсканированных страниц кучи) для типичного запроса. НайдитеHeap Blocks: lossy=n
в плане выполненияpages_per_range=1
и сравните с другими значениямиpages_per_range
- то есть посмотрите, сколько ненужных блоков кучи сканируется.Возможно, стоит рассмотреть GIN, но, вероятно, не GiST - однако, если естественная кластеризация действительно хороша, то BRIN, вероятно, будет лучшим выбором.
Вот пример сравнения между различными типами индексов для фиктивных данных, немного похожих на ваш:
таблица и индексы:
размеры отношения:
покрытие btree:
обычный btree:
BRIN pages_per_range = 4:
BRIN pages_per_range = 2:
ДЖИН:
dbfiddle здесь
источник
Bitmap Index Scan
как «читайте весь индекс Брина», но, возможно, это неправильное чтение. OracleCOMPRESS
выглядит как то, что было бы здесь полезно, так как это уменьшило бы размер B-дерева, но я застрял с pg!Помимо btree и brin, которые кажутся наиболее разумными вариантами, есть некоторые другие, экзотические варианты, которые, возможно, стоит изучить - они могут быть полезны или нет в вашем случае:
INCLUDE
индексов . Надеюсь, что они появятся в следующей основной версии (10) Postgres, где-то в сентябре 2017 года. Индекс on(a) INCLUDE (b)
имеет ту же структуру, что и индекс,(a)
но включает в листовые страницы все значенияb
(но неупорядоченные). Это означает, что вы не можете использовать его, например, дляSELECT * FROM t WHERE a = 'a' AND b = 2 ;
. Индекс можно использовать, но хотя(a,b)
индекс найдет совпадающие строки с помощью одного запроса, включаемый индекс должен будет пройти через (возможно, 100 КБ, как в вашем случае) значения, которые соответствуют,a = 'a'
и проверитьb
значения.С другой стороны, индекс немного менее широк, чем
(a,b)
индекс, и вам не нужен порядокb
для запроса, чтобы вычислитьSUM(b)
. Вы также можете иметь, например,(a) INCLUDE (b,c,d)
который можно использовать для запросов, похожих на ваш, которые объединяются по всем 3 столбцам.Отфильтрованные (частичные) индексы . На первый взгляд это может показаться немного сумасшедшим * :
Один индекс для каждого
a
значения. В вашем случае около 100К индексов. Хотя это звучит много, учтите, что каждый индекс будет очень маленьким, как по размеру (количеству строк), так и по ширине (поскольку он будет хранить толькоb
значения). Однако во всех других аспектах он (индексы 100К вместе) будет действовать как индекс b-дерева(a,b)
при использовании пространства(b)
индекса.Недостатком является то, что вам придется создавать и поддерживать их самостоятельно, каждый раз, когда
a
в таблицу добавляется новое значение . Поскольку ваша таблица довольно стабильна, без многих (или каких-либо) вставок / обновлений, это не кажется проблемой.Сводные таблицы. Поскольку таблица достаточно стабильна, вы всегда можете создать и заполнить сводную таблицу наиболее распространенными агрегатами, которые вам понадобятся (
sum(b), sum(c), sum(d), avg(b), count(distinct b)
и т. Д.). Он будет небольшим (всего 100 тыс. Строк) и будет заполняться только один раз и обновляться только тогда, когда строки будут добавлены / обновлены / удалены в основной таблице.*: идея скопирована с этой компании, которая управляет 10 миллионами индексов в своей производственной системе: Куча: запуск 10 миллионов индексов Postgresql в производстве (и подсчет) .
источник
SUM
в качестве примера, но на практике мои запросы не могут быть предварительноselect ... from t where a = '?' and ??
вычислены (они больше похожи на то,??
что wjere было бы некоторым другим определяемым пользователем условием.??
есть;)DO
заявления в этом связанном ответе .