Запрос PostgreSQL не использует индекс в производстве

Я замечаю что-то странное/непонятное:

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

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

EDIT: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 ответов


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

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

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

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

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

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

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

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

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

ответить оригиналу вопрос:

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

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


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

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

edit:

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

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

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

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

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

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


попробовать

SET enable_seqscan TO 'off'

до EXPLAIN ANALYZE


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

развитие:

->  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? Is number_returned_rows / number_total_rows сопоставимый между dev и prod?

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