Индексирование столбца, используемого для ORDER BY, с ограничением в PostgreSQL - PullRequest
1 голос
/ 24 сентября 2010

У меня есть скромная таблица из примерно 10 тыс. Строк, которая часто сортируется по столбцу с именем «name». Итак, я добавил индекс для этого столбца. Теперь выбирает на это быстро:

EXPLAIN ANALYZE SELECT * FROM crm_venue ORDER BY name ASC LIMIT 10;
  ...query plan...
 Limit  (cost=0.00..1.22 rows=10 width=154) (actual time=0.029..0.065 rows=10 loops=1)
   ->  Index Scan using crm_venue_name on crm_venue  (cost=0.00..1317.73 rows=10768     width=154) (actual time=0.026..0.050 rows=10 loops=1)
 Total runtime: 0.130 ms

Если я увеличу LIMIT до 60 (это примерно то, что я использую в приложении), общее время выполнения не увеличится намного дальше.

Поскольку я использую «шаблон логического удаления» в этой таблице, я рассматриваю только те записи, где delete_date NULL. Так что это общий выбор, который я делаю:

SELECT * FROM crm_venue WHERE delete_date IS NULL ORDER BY name ASC LIMIT 10;

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

CREATE INDEX name_delete_date_null ON crm_venue (name) WHERE delete_date IS NULL;

Теперь можно быстро упорядочить с помощью ограничения логического удаления:

EXPLAIN ANALYZE SELECT * FROM crm_venue WHERE delete_date IS NULL ORDER BY name ASC LIMIT 10;
 Limit  (cost=0.00..84.93 rows=10 width=154) (actual time=0.020..0.039 rows=10 loops=1)
   ->  Index Scan using name_delete_date_null on crm_venue  (cost=0.00..458.62 rows=54 width=154) (actual time=0.018..0.033 rows=10 loops=1)
 Total runtime: 0.076 ms

Отлично! Но это то, где я попал в беду. Приложение редко вызывает первые 10 строк. Итак, давайте выберем еще несколько строк:

EXPLAIN ANALYZE SELECT * FROM crm_venue WHERE delete_date IS NULL ORDER BY name ASC LIMIT 20;

 Limit  (cost=135.81..135.86 rows=20 width=154) (actual time=18.171..18.189 rows=20 loops=1)
   ->  Sort  (cost=135.81..135.94 rows=54 width=154) (actual time=18.168..18.173 rows=20 loops=1)
     Sort Key: name
     Sort Method:  top-N heapsort  Memory: 21kB
     ->  Bitmap Heap Scan on crm_venue  (cost=4.67..134.37 rows=54 width=154) (actual time=2.355..8.126 rows=10768 loops=1)
           Recheck Cond: (delete_date IS NULL)
           ->  Bitmap Index Scan on crm_venue_delete_date_null_idx  (cost=0.00..4.66 rows=54 width=0) (actual time=2.270..2.270 rows=10768 loops=1)
                 Index Cond: (delete_date IS NULL)
 Total runtime: 18.278 ms

Как вы можете видеть, оно идет от 0,1 мс до 18 !!

Понятно, что происходит то, что упорядочение больше не может использовать индекс для запуска сортировки. Я заметил, что когда я увеличиваю число LIMIT с 20 до более высоких чисел, это всегда занимает около 20-25 мс.

Я делаю это неправильно или это ограничение PostgreSQL? Как лучше всего настроить индексы для этого типа запросов?

Ответы [ 2 ]

1 голос
/ 24 сентября 2010

Я полагаю, что, поскольку, по логике, индекс состоит из указателей на набор строк на множестве страниц данных.если вы извлекаете страницу, о которой известно, что на ней есть ТОЛЬКО «удаленные» записи, ей не нужно перепроверять страницу после ее извлечения, чтобы получить только те записи, которые были удалены.

Следовательно, может случиться так, что когда вы выполняете LIMIT 10 и упорядочиваете по имени, все первые 10, которые возвращаются из индекса, находятся на странице данных (или страницах), которая состоит только из удаленных записей.Так как он знает, что эти страницы являются однородными, ему не нужно перепроверять их после извлечения их с диска.Как только вы увеличите до LIMIT 20, по крайней мере один из первых 20 будет на смешанной странице с не удаленными записями.Затем это заставит исполнителя перепроверить каждую запись, поскольку он не может извлечь страницы данных с шагом менее 1 страницы с диска или из кэша.

В качестве эксперимента, если вы можете создать индекс (delete_date, имя) и введите команду CLUSTER crm_venue ON, где индекс - это ваш новый индекс.Это должно перестроить таблицу в порядке сортировки delete_date затем name.Просто чтобы быть уверенным в себе, вы должны затем выполнить команду REINDEX TABLE crm_venue.Теперь попробуйте запрос еще раз.Поскольку все NOT NULL будут кластеризованы вместе на диске, это может работать быстрее при больших значениях LIMIT.

Конечно, это все просто теория, так что YMMV ...

0 голосов
/ 24 сентября 2010

При увеличении количества строк изменяется количество элементов индекса.Я не уверен, но может случиться так, что, поскольку он использует большее количество строк в таблице, ему нужно будет прочитать достаточно блоков таблицы, чтобы этих плюс блоков индекса было достаточно, чтобы индекс больше не имел смысла использовать.Это может быть просчет планировщиком.Также ваше имя (поле индексируется) не является полем, ограничивающим область индекса, которая может нанести ущерб планировщику математики.

Попробовать: увеличить процентную долю таблицы, которая учитывается при построении статистики,Ваши данные могут быть искажены таким образом, что статистика не соберет истинную репрезентативную выборку.

Индексируйте все строки, а не только строки NULL, посмотрите, что лучше.Вы даже можете попробовать индексировать, где NOT NULL.

Кластер на основе индекса в этом поле, чтобы уменьшить требуемые блоки данных и превратить его в сканирование диапазона.

Нули и индексы не всегда воспроизводятсяотлично.Попробуйте другой способ:

alter table crm_venue add column char delete_flag;
update crm_venue set delete flag='Y' where delete_date is not null;
update crm_venue set delete flag='N' where delete_date is null;
create index deleted_venue (delete_flag) where delete_flag = 'N';
SELECT * FROM crm_venue WHERE delete__flag='Y' ORDER BY name ASC LIMIT 20;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...