Запрос PostgreSQL не использует индекс в производстве - PullRequest
9 голосов
/ 28 февраля 2012

Я заметил что-то странное / странное:

Точно такой же запрос в разработке / производстве не использует один и тот же путь запроса.В частности, в версии для разработки используются индексы, которые не используются в производстве (в пользу seqscan).

Единственное реальное отличие состоит в том, что набор данных для производства значительно больше - размер индекса составляет 1034 МБ по сравнению с29 МБ в производстве.Будет ли PostgreSQL воздерживаться от использования индексов, если они (или таблица) слишком велики?

РЕДАКТИРОВАНИЕ: EXPLAIN ANALYZE для обоих запросов:

Разработка:

Limit  (cost=41638.15..41638.20 rows=20 width=154) (actual time=159.576..159.581 rows=20 loops=1)
  ->  Sort  (cost=41638.15..41675.10 rows=14779 width=154) (actual time=159.575..159.577 rows=20 loops=1)
        Sort Key: (sum(scenario_ad_group_performances.clicks))
        Sort Method: top-N heapsort  Memory: 35kB
        ->  GroupAggregate  (cost=0.00..41244.89 rows=14779 width=154) (actual time=0.040..151.535 rows=14197 loops=1)
              ->  Nested Loop Left Join  (cost=0.00..31843.75 rows=93800 width=154) (actual time=0.022..82.509 rows=50059 loops=1)
                    ->  Merge Left Join  (cost=0.00..4203.46 rows=14779 width=118) (actual time=0.017..27.103 rows=14197 loops=1)
                          Merge Cond: (scenario_ad_groups.id = scenario_ad_group_vendor_instances.ad_group_id)
                          ->  Index Scan using scenario_ad_groups_pkey on scenario_ad_groups  (cost=0.00..2227.06 rows=14779 width=114) (actual time=0.009..12.085 rows=14197 loops=1)
                                Filter: (scenario_id = 22)
                          ->  Index Scan using index_scenario_ad_group_vendor_instances_on_ad_group_id on scenario_ad_group_vendor_instances  (cost=0.00..1737.02 rows=27447 width=8) (actual time=0.007..7.021 rows=16528 loops=1)
                                Filter: (ad_vendor_id = ANY ('{1,2,3}'::integer[]))
                    ->  Index Scan using index_ad_group_performances_on_vendor_instance_id_and_date on scenario_ad_group_performances  (cost=0.00..1.73 rows=11 width=44) (actual time=0.002..0.003 rows=3 loops=14197)
                          Index Cond: ((vendor_instance_id = scenario_ad_group_vendor_instances.id) AND (date >= '2012-02-01'::date) AND (date <= '2012-02-28'::date))
Total runtime: 159.710 ms

Производство:

Limit  (cost=822401.35..822401.40 rows=20 width=179) (actual time=21279.547..21279.591 rows=20 loops=1)
  ->  Sort  (cost=822401.35..822488.42 rows=34828 width=179) (actual time=21279.543..21279.560 rows=20 loops=1)
        Sort Key: (sum(scenario_ad_group_performances.clicks))
        Sort Method: top-N heapsort  Memory: 33kB
        ->  GroupAggregate  (cost=775502.60..821474.59 rows=34828 width=179) (actual time=19126.783..21226.772 rows=34495 loops=1)
              ->  Sort  (cost=775502.60..776739.48 rows=494751 width=179) (actual time=19125.902..19884.164 rows=675253 loops=1)
                    Sort Key: scenario_ad_groups.id
                    Sort Method: external merge  Disk: 94200kB
                    ->  Hash Right Join  (cost=25743.86..596796.70 rows=494751 width=179) (actual time=1155.491..16720.460 rows=675253 loops=1)
                          Hash Cond: (scenario_ad_group_performances.vendor_instance_id = scenario_ad_group_vendor_instances.id)
                          ->  Seq Scan on scenario_ad_group_performances  (cost=0.00..476354.29 rows=4158678 width=44) (actual time=0.043..8949.640 rows=4307019 loops=1)
                                Filter: ((date >= '2012-02-01'::date) AND (date <= '2012-02-28'::date))
                          ->  Hash  (cost=24047.72..24047.72 rows=51371 width=143) (actual time=1123.896..1123.896 rows=34495 loops=1)
                                Buckets: 1024  Batches: 16  Memory Usage: 392kB
                                ->  Hash Right Join  (cost=6625.90..24047.72 rows=51371 width=143) (actual time=92.257..1070.786 rows=34495 loops=1)
                                      Hash Cond: (scenario_ad_group_vendor_instances.ad_group_id = scenario_ad_groups.id)
                                      ->  Seq Scan on scenario_ad_group_vendor_instances  (cost=0.00..11336.31 rows=317174 width=8) (actual time=0.020..451.496 rows=431770 loops=1)
                                            Filter: (ad_vendor_id = ANY ('{1,2,3}'::integer[]))
                                      ->  Hash  (cost=5475.55..5475.55 rows=34828 width=139) (actual time=88.311..88.311 rows=34495 loops=1)
                                            Buckets: 1024  Batches: 8  Memory Usage: 726kB
                                            ->  Bitmap Heap Scan on scenario_ad_groups  (cost=798.20..5475.55 rows=34828 width=139) (actual time=4.451..44.065 rows=34495 loops=1)
                                                  Recheck Cond: (scenario_id = 276)
                                                  ->  Bitmap Index Scan on index_scenario_ad_groups_on_scenario_id  (cost=0.00..789.49 rows=34828 width=0) (actual time=4.232..4.232 rows=37006 loops=1)
                                                        Index Cond: (scenario_id = 276)
Total runtime: 21306.697 ms

Ответы [ 4 ]

15 голосов
/ 28 февраля 2012

Отказ от ответственности

Я очень мало использовал PostgreSQL.Я отвечаю, основываясь на своих знаниях об использовании индексов SQL Server и планах выполнения.Я прошу у богов PostgreSQL пощады, если я что-то не так.

Оптимизаторы запросов являются динамическими

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

Когда было бы более эффективно использовать сканирование таблицы по сравнению со сканированием индекса?

SELECT A, B
FROM someTable
WHERE A = 'SOME VALUE'

Допустим, у вас есть некластеризованный индекс для столбца A.В этом случае вы фильтруете по столбцу A, который потенциально может использовать индекс.Это было бы эффективно, если индекс достаточно избирателен - в основном, сколько различных значений составляют индекс?База данных хранит статистику по этой информации о селективности и использует эту статистику при расчете затрат для планов выполнения.

Если в таблице миллион строк, но только 10 возможных значений для A, то ваш запрос скорее всеговернуть около 100К строк.Поскольку индекс не является кластеризованным, и вы возвращаете столбцы, не включенные в индекс, B, необходимо выполнить поиск для каждой возвращаемой строки.Эти поиски являются поисками с произвольным доступом, которые намного дороже, чем последовательное чтение, используемое при сканировании таблицы.В определенный момент для базы данных становится более эффективным просто выполнять сканирование таблицы, а не сканирование индекса.

Это всего лишь один сценарий, есть много других.Трудно понять, не зная больше о том, на что похожи ваши данные, как выглядят ваши индексы и как вы пытаетесь получить к ним доступ.

Чтобы ответить на оригинальный вопрос :

Будет ли PostgreSQL воздерживаться от использования индексов, если они (или таблица) слишком велики?Нет. Более вероятно, что для доступа к данным PostgreSQL будет менее эффективно использовать индекс по сравнению с использованием сканирования таблицы.

Часто задаваемые вопросы PostgreSQL затрагивают эту тему (см .: Почему мои запросы медленные? Почему они не используют мои индексы? ): https://wiki.postgresql.org/wiki/FAQ#Why_are_my_queries_slow.3F_Why_don.27t_they_use_my_indexes.3F

3 голосов
/ 28 февраля 2012

Попробуйте

SET enable_seqscan TO 'off'

до EXPLAIN ANALYZE

3 голосов
/ 28 февраля 2012

Оптимизатор запросов Postgres предлагает несколько сценариев (например, индекс vs seq-scan) и оценивает их, используя статистическую информацию о ваших таблицах и относительную стоимость доступа к диску / памяти / индексу / таблице, заданную в конфигурации.

Использовали ли вы команду EXPLAIN, чтобы увидеть, почему использование индекса было опущено?Вы использовали EXPLAIN ANALYZE, чтобы узнать, было ли решение ошибочным?Можем ли мы увидеть результаты, пожалуйста?

edit:

Как бы сложно ни было проанализировать два разных единичных запроса в разных системах, я думаю, что вижу пару вещей.

Фактическая / себестоимость производственной среды составляет около 20-100 миллисекунд на единицу затрат.Я даже не администратор, но это кажется последовательным.Среда разработки имеет 261 для основного запроса.Это кажется правильным?Ожидаете ли вы, что предварительная скорость (память / диск / ЦП) производственной среды будет в 2-10 раз выше, чем у dev?

Поскольку производственная среда имеет гораздо более сложный план запросов ,похоже, он делает свою работу.Несомненно, план среды разработки и многие другие были рассмотрены и считаются слишком дорогостоящими.И разница в 20-100 не так уж много в моем опыте (но, опять же, не в DBA) и показывает, что нет ничего необычного.Тем не менее, вы можете запустить VACUUM на БД на всякий случай.

Я не достаточно опытен и терпелив, чтобы декодировать полный запрос, но может быть точка денормализации / NOSQL-iization дляОптимизация?

Самым большим узким местом является слияние дисков на 90 МБ.Если в производственной среде достаточно памяти, вы можете увеличить соответствующий параметр (рабочая память?), Чтобы сделать это в памяти.Кажется, это work_mem параметр здесь , хотя вы захотите прочитать остальные.

Я бы также предложил посмотреть на статистика использования индекса .Существует множество вариантов с частичными и функциональными индексами.

2 голосов
/ 28 февраля 2012

Мне кажется, что ваши данные разработчика намного "проще", чем производственные данные. Как пример:

Разработка:

->  Index Scan using index_scenario_ad_group_vendor_instances_on_ad_group_id on scenario_ad_group_vendor_instances  
(cost=0.00..1737.02 rows=27447 width=8) 
(actual time=0.007..7.021 rows=16528 loops=1)
Filter: (ad_vendor_id = ANY ('{1,2,3}'::integer[]))

Производство:

->  Seq Scan on scenario_ad_group_vendor_instances  
(cost=0.00..11336.31 rows=317174 width=8) 
(actual time=0.020..451.496 rows=431770 loops=1)
Filter: (ad_vendor_id = ANY ('{1,2,3}'::integer[]))

Это означает, что в dev 27447 соответствующие строки были оценены заранее, и действительно было найдено 16528 строк. Это не то же самое и ОК.

В производстве 317174 подходящих строк были оценены заранее, и было найдено 431770 строк. Также хорошо.

Но сравнение dev с prod означает, что числа различаются в 10 раз. Как уже указывают другие ответы, выполнение случайного поиска в 10 раз (из-за доступа к индексу) действительно может быть хуже, чем сканирование простой таблицы.

Следовательно, интересен вопрос: сколько строк в этой таблице содержится как в dev, так и в prod? number_returned_rows / number_total_rows сопоставимо между dev и prod?

Редактировать Не забывайте: я выбрал один индексный доступ в качестве примера. Быстрый взгляд показывает, что другие обращения к индексу имеют те же симптомы.

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