Почему postresql не использует все столбцы в многоколоночном индексе? - PullRequest
2 голосов
/ 10 июля 2019

Я использую расширение

CREATE EXTENSION btree_gin;

У меня есть индекс, который выглядит следующим образом ...

create index boundaries2 on rets USING GIN(source, isonlastsync, status, (geoinfo::jsonb->'boundaries'), ctcvalidto, searchablePrice, ctcSortOrder);

До того, как я начал с ним связываться, индекс выглядел примерно так, с теми же результатами, которыми я собираюсь поделиться, поэтому, кажется, незначительные изменения в определении индекса не имеют значения:

create index boundaries on rets USING GIN((geoinfo::jsonb->'boundaries'), source, status, isonlastsync, ctcvalidto, searchablePrice, ctcSortOrder);

Я задаю pgsql 11 этот запрос:

explain analyze select id from rets where ((geoinfo::jsonb->'boundaries' ?| array['High School: Torrey Pines']) AND source='SDMLS'
          AND searchablePrice>=800000 AND searchablePrice<=1200000 AND YrBlt>=2000 AND EstSF>=2300
         AND Beds>=3 AND FB>=2 AND ctcSortOrder>'2019-07-05 16:02:54 UTC' AND Status IN ('ACTIVE','BACK ON MARKET')
         AND ctcvalidto='9999-12-31 23:59:59 UTC' AND isonlastsync='true') order by LstDate desc, ctcSortOrder desc LIMIT 3000;

с результатом ...

 Limit  (cost=120.06..120.06 rows=1 width=23) (actual time=472.849..472.850 rows=1 loops=1)
   ->  Sort  (cost=120.06..120.06 rows=1 width=23) (actual time=472.847..472.848 rows=1 loops=1)
         Sort Key: lstdate DESC, ctcsortorder DESC
         Sort Method: quicksort  Memory: 25kB
         ->  Bitmap Heap Scan on rets  (cost=116.00..120.05 rows=1 width=23) (actual time=472.748..472.841 rows=1 loops=1)
               Recheck Cond: ((source = 'SDMLS'::text) AND (((geoinfo)::jsonb -> 'boundaries'::text) ?| '{"High School: Torrey Pines"}'::text[]) AND (ctcvalidto = '9999-12-31 23:59:59+00'::timestamp with time zone) AND (searchableprice >= 800000) AND (searchableprice <= 1200000) AND (ctcsortorder > '2019-07-05 16:02:54+00'::timestamp with time zone))
               Rows Removed by Index Recheck: 93
               Filter: (isonlastsync AND (yrblt >= 2000) AND (estsf >= 2300) AND (beds >= 3) AND (fb >= 2) AND (status = ANY ('{ACTIVE,"BACK ON MARKET"}'::text[])))
               Rows Removed by Filter: 10
               Heap Blocks: exact=102
               ->  Bitmap Index Scan on boundaries2  (cost=0.00..116.00 rows=1 width=0) (actual time=471.762..471.762 rows=104 loops=1)
                     Index Cond: ((source = 'SDMLS'::text) AND (((geoinfo)::jsonb -> 'boundaries'::text) ?| '{"High School: Torrey Pines"}'::text[]) AND (ctcvalidto = '9999-12-31 23:59:59+00'::timestamp with time zone) AND (searchableprice >= 800000) AND (searchableprice <= 1200000) AND (ctcsortorder > '2019-07-05 16:02:54+00'::timestamp with time zone))
 Planning Time: 0.333 ms
 Execution Time: 474.311 ms
(14 rows)

Вопрос

Почему столбцы status и isonlastsync не используются Bitmap Index Scan on boundaries2?

Ответы [ 3 ]

1 голос
/ 10 июля 2019

Это можно сделать, если он предсказывает, что фильтрация этих столбцов будет быстрее. Это обычно имеет место, если количество элементов в столбцах очень мало, и вы получите достаточно большую часть всех строк; это верно для boolean подобно isonlastsync и обычно верно для столбцов состояния с несколькими отличительными значениями.

Rows Removed by Filter: 10 это очень мало для фильтрации, потому что ваша таблица не содержит большого количества строк, или большинство из них соответствуют условиям, которые вы указали для этих двух столбцов. Вы можете попытаться сгенерировать больше данных в этой таблице или выбрать строки с редким статусом.

Я предлагаю сделать частичные индексы (с условием WHERE), по крайней мере, для значения boolean и удалить эти два столбца, чтобы сделать этот индекс немного более легким.

0 голосов
/ 10 июля 2019

Я думаю, что вы задаете себе неправильный вопрос. Как уже ответил Лукаш, PostgreSQL может оказаться неэффективным для проверки всех столбцов в индексе. Проблема в том, что у вас слишком большой индекс на диске.

Вероятно, пытаясь ускорить этот SQL, вы добавили в индекс как можно больше столбцов, но это не дает результата.

Хитрость заключается в том, чтобы понять, сколько данных PostgreSQL должен прочитать, чтобы найти ваши записи. Если ваш индекс содержит слишком много данных, ему придется много читать. Также следует помнить, что столбцы с низким количеством элементов кардинально не работают с BTree и типами общих индексов; как правило, вы хотите избежать их индексации.

Чтобы иметь как можно меньший индекс и быстро выполнять поиск, вы должны найти столбец с большим количеством элементов или, что лучше, столбец, который будет возвращать меньше строк для вашего запроса. Я думаю, это "ctcSortOrder". Это будет первый столбец вашего индекса.

Теперь посмотрите, после фильтрации по 1-му столбцу, какой столбец имеет наибольшее количество элементов или отфильтрует большинство строк. Я понятия не имею о ваших данных, но «источник» выглядит хорошим кандидатом.

Старайтесь избегать поисков jsonb, если они не являются основным источником мощности, и сохраняйте индекс как Btree. BTree в несколько раз быстрее.

И, как предложил Лукаш, посмотрите на частичные индексы. Например, добавьте «WHERE Status IN (« ACTIVE »,« BACK ON MARKET ») И« isonlastsync = «true» », так как эти два параметра могут быть общими для всех ваших запросов.

Суть в том, что более простой, меньший индекс быстрее, чем индексируемые все столбцы. И порядок столбцов имеет большое значение. Придерживайтесь BTree, если на то нет веской причины (много кардинальных типов, не совместимых с btree).

Если ваша таблица огромная (> 10 миллионов строк), рассмотрите возможность разбиения таблицы, например ctcSortOrder. Но я не думаю, что это ваш случай.

0 голосов
/ 10 июля 2019

Я не могу сказать вам, почему, но я могу помочь вам оптимизировать запрос.

Вы должны не использовать многостолбцовый индекс GIN, но индекс GIN только для jsonb выражение и индекс b-дерева для других столбцов.

Порядок столбцов имеет значение: сначала поместите oned, используемый в условии равенства, с наиболее избирательным в начале.Затем поместите столбец с обязательным выборочным неравенством или условиями IN.Для следующих столбцов порядок не имеет значения, поскольку они будут действовать только как фильтры при сканировании индекса.

Убедитесь, что индексы кэшируются в ОЗУ.

Я бы ожидалчтобы ты был быстрее таким образом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...