Последовательное получение плана запросов postgres для конкретного запроса для использования индекса, а не сканирования

Я некоторое время работал над тем, чтобы выяснить, как заставить планирование запросов действовать немного умнее, но безуспешно. Я возился с work_mem и друзьями, много запускал vacumm analyze и пытался изменить запрос с помощью order by. Я включил 3 прогона одного и того же запроса с разными смещениями. У меня сложилось впечатление, что этот запрос не так эффективен, как мог бы быть. Какие-нибудь мысли?

На всякий случай, если это не бросается в глаза — единственное изменение между следующими запросами — это offset

bloomapi=# explain analyze SELECT * FROM npis WHERE provider_last_name_legal_name = 'THOMPSON' offset 250 limit 10;
                                                                             QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=965.13..998.97 rows=10 width=2589) (actual time=568.458..577.507 rows=10 loops=1)
   ->  Bitmap Heap Scan on npis  (cost=119.15..20382.11 rows=5988 width=2589) (actual time=58.140..577.027 rows=260 loops=1)
         Recheck Cond: ((provider_last_name_legal_name)::text = 'THOMPSON'::text)
         ->  Bitmap Index Scan on npis_temp_provider_last_name_legal_name_idx1  (cost=0.00..117.65 rows=5988 width=0) (actual time=36.819..36.819 rows=5423 loops=1)
               Index Cond: ((provider_last_name_legal_name)::text = 'THOMPSON'::text)
 Total runtime: 578.301 ms
(6 rows)

bloomapi=# explain analyze SELECT * FROM npis WHERE provider_last_name_legal_name = 'THOMPSON' offset 100 limit 10;
                                                                             QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=395.81..435.40 rows=10 width=2589) (actual time=0.397..0.440 rows=10 loops=1)
   ->  Index Scan using npis_temp_provider_last_name_legal_name_idx1 on npis  (cost=0.00..23701.38 rows=5988 width=2589) (actual time=0.063..0.293 rows=110 loops=1)
         Index Cond: ((provider_last_name_legal_name)::text = 'THOMPSON'::text)
 Total runtime: 0.952 ms
(4 rows)

bloomapi=# explain analyze SELECT * FROM npis WHERE provider_last_name_legal_name = 'THOMPSON' offset 4100 limit 10;
                                                                            QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=13993.25..14027.09 rows=10 width=2589) (actual time=9356.723..9400.021 rows=10 loops=1)
   ->  Bitmap Heap Scan on npis  (cost=119.15..20382.11 rows=5988 width=2589) (actual time=2.968..9393.327 rows=4110 loops=1)
         Recheck Cond: ((provider_last_name_legal_name)::text = 'THOMPSON'::text)
         ->  Bitmap Index Scan on npis_temp_provider_last_name_legal_name_idx1  (cost=0.00..117.65 rows=5988 width=0) (actual time=1.943..1.943 rows=5423 loops=1)
               Index Cond: ((provider_last_name_legal_name)::text = 'THOMPSON'::text)
 Total runtime: 9400.426 ms
(6 rows)

Некоторые важные примечания:

  • Я очистил разделяемую память в системе перед выполнением первого запроса, поэтому на некоторое фактическое время первого запроса, вероятно, повлияла загрузка индекса.
  • данные широкие и разреженные - 329 столбцов, многие из которых представляют собой пустые символы (30)
  • данные практически доступны только для чтения — они обновляются еще 15 000 строк раз в неделю.
  • производительность этих запросов была на самом деле выше для тех же запросов, когда были настройки БД по умолчанию, поставляемые с Ubuntu ppa (в данный момент у меня нет этих планов запросов, но я мог бы покопаться в них, если в противном случае ничего очевидного не выскочит). Параметры, которые были изменены по сравнению со значениями по умолчанию: shared_buffers = 256 МБ, Effective_cache_size = 512 МБ, checkpoint_segments = 64, checkpoint_completion_target = 0,9, default_statistics_target = 500
  • фактические данные около 4 миллионов строк / 1,29 ГБ для таблицы отдельно, provider_last_name_legal_name индексируется btree - размер индекса составляет 95 МБ. около 3/4 строк имеют ненулевое значение в этом столбце, а вся таблица имеет 488 тыс. различных значений.

person Michael Wasser    schedule 14.09.2013    source источник
comment
Вы пытались установить random_page_cost на более низкое значение (~ 1,5)? Кстати: какие у вас настройки для work_mem Плюс: effective_cache_size = 512M кажется довольно низким; ваша таблица размером 1,3 ГБ должна (почти) соответствовать ядру, по крайней мере, индексу.   -  person wildplasser    schedule 15.09.2013
comment
Кстати: я не одобряю LIMIT/OFFSET без ORDER BY. В текстовом столбце, который тоже индексируется. Какова мощность provider_last_name_legal_name ? Наконец: размер 1.2G/4Mrows:= 300 байт байт/строка, что кажется немного большим. Кстати: как уместить 329 столбцов в 300 байт?   -  person wildplasser    schedule 15.09.2013
comment
@wildplasser, устанавливающий более низкое значение random_page_cost, делает сканирование индекса оценки планировщика более быстрым для более высоких смещений, но планировщик по-прежнему переключается на смещение около 700 с random_page_cost = 1,5. При этом кажется, что даже производительность сканирования индекса страдает при большом смещении. Чтобы ответить на подгонку 329 столбцов в 300 байтов - большинство столбцов пустые/таблица очень разреженная.   -  person Michael Wasser    schedule 15.09.2013
comment
@wildplasser Кроме того, количество элементов не очень высокое / низкое - 488 тыс. различных значений в 3 млн из 4 млн строк (последний 1 млн строк пуст). Это таблица людей с фамилиями в США, так что, вероятно, она имеет частотное распределение, подобное именам. mongabay.com/most_common_surnames.htm   -  person Michael Wasser    schedule 15.09.2013
comment
Я знаю степенное распределение. Но плохая новость заключается в том, что учитывая 300+ столбцов, у вас могут возникнуть проблемы с моделированием данных. (лично я даже не могу представить, чтобы 300+ столбцов были полностью независимыми/ортогональными. Такое может происходить в высшей математике, даже не в физике)   -  person wildplasser    schedule 16.09.2013
comment
OTOH: Я не могу представить, чтобы 300+ столбцов функционально зависели от первичного ключа (есть ли в вашей таблице имеет PK?) Если они есть: я не могу себе этого представить без подструктуры (2NF, 3NF , BCNF). Не с 300+ столбцами.   -  person wildplasser    schedule 16.09.2013
comment
@wildplasser Я извлекаю свои данные из источника данных, в котором нет концепции денормализации. Я решил не денормализовать данные для повышения производительности импорта и сохранения чрезвычайно низкой сложности. Есть первичный ключ, но в эту таблицу впихнуто множество различных сущностей. Этот подход сработал для меня с этим постом в качестве исключения (что само по себе является исключительным случаем). С точки зрения того, как я использую данные, я на самом деле проецирую данные так, чтобы они выглядели как нормализованная структура. Если вы все еще заинтересованы, загляните на www.bloomapi.com с исходным кодом на github.com/untoldone/bloomapi.   -  person Michael Wasser    schedule 16.09.2013
comment
Я проверил определение таблицы на github, и оно определенно ужасно (в нем есть группа, повторяющаяся 50 раз!) Если это только импортная/промежуточная таблица, ИМХО первое, что нужно сделать, это заключаться в том, чтобы вытащить соответствующие части в нормализованные целевые таблицы (которые также могут быть временными). Все это, а не LIMIT. После этого: проверка согласованности и, возможно, хранилище данных.   -  person wildplasser    schedule 18.09.2013


Ответы (1)


Мое обоснованное предположение состоит в том, что большие смещения запускают эти планы. Даже если вы ограничиваете результаты десятью строками, PostgreSQL должен учитывать все предыдущие строки. Я подозреваю, что когда вы удаляете offset (например, используете limit 260 в первом запросе), вы увидите похожие времена выполнения.

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

set enable_bitmapscan = false;
person Chris Bandy    schedule 15.09.2013
comment
Спасибо! Хотя это не решило мою проблему с производительностью, оно дало ответ на мой вопрос/помогло мне отладить и выяснить, что используемые планы запросов не были такими глупыми. Сканирование индекса с большими смещениями также было медленным. Там, где на самом деле переключение было правильным, было гораздо большее смещение, но в целом смещения кажутся просто низкими производительными операциями (см. ответ Тома Лейна-2 на postgresql.1045698.n5.nabble.com/). Возможно, мне придется найти более творческий способ запроса или просто согласиться с низкой производительностью для этого типа запроса. - person Michael Wasser; 15.09.2013