Подтвердить что ты не робот

Как понять ANPLYIN ANPLYIN

Я не очень хорошо разбираюсь в результатах EXPLAIN ANALYZE, у меня есть огромная проблема с медленными запросами. Я попытался прочитать, как интерпретировать результаты запросов объяснения, но я до сих пор не знаю, что мне нужно искать, и что может быть неправильным. У меня такое ощущение, что где-то там какой-то большой красный свет, я просто не вижу его.

Итак, запрос довольно прост, он выглядит так:

EXPLAIN ANALYZE SELECT "cars".* FROM "cars" WHERE "cars"."sales_state" = 'onsale' AND "cars"."brand" = 'BMW' AND "cars"."model_name" = '318i' AND "cars"."has_auto_gear" = TRUE  LIMIT 25 OFFSET 0

И результат выглядит следующим образом:

Limit  (cost=0.00..161.07 rows=25 width=1245) (actual time=35.232..38.694 rows=25 loops=1)
  ->  Index Scan using index_cars_onsale_on_brand_and_model_name on cars  (cost=0.00..1179.06 rows=183 width=1245) (actual time=35.228..38.652 rows=25 loops=1)
        Index Cond: (((brand)::text = 'BMW'::text) AND ((model_name)::text = '318i'::text))
        Filter: has_auto_gear"
Total runtime: 38.845 ms

Небольшой фон: Я на Postgresql 9.1.6, работая на выделенных базах данных Herokus. Мой db имеет aprox 7,5 ГБ оперативной памяти, таблицы автомобилей содержат 3,1M строк, а aprox 2,0M строк имеет sales_state = 'onsale'. Стол имеет 170 столбцов. Индекс, который он использует, выглядит примерно так:

CREATE INDEX index_cars_onsale_on_brand_and_model_name
  ON cars
  USING btree
  (brand COLLATE pg_catalog."default" , model_name COLLATE pg_catalog."default" )
  WHERE sales_state::text = 'onsale'::text;

Кто-нибудь видит какую-то большую очевидную проблему?

EDIT:

SELECT pg_relation_size('cars'), pg_total_relation_size('cars');

pg_relation_size: 2058444800 pg_total_relation_size: 4900126720

SELECT pg_relation_size('index_cars_onsale_on_brand_and_model_name');

pg_relation_size: 46301184

SELECT avg(pg_column_size(cars)) FROM cars limit 5000;

avg: 636.9732567210792995

БЕЗ ПРЕДЕЛА:

EXPLAIN ANALYZE SELECT "cars".* FROM "cars" WHERE "cars"."sales_state" = 'onsale' AND "cars"."brand" = 'BMW' AND "cars"."model_name" = '318i' AND "cars"."has_auto_gear" = TRUE

Bitmap Heap Scan on cars  (cost=12.54..1156.95 rows=183 width=4) (actual time=17.067..55.198 rows=2096 loops=1)
  Recheck Cond: (((brand)::text = 'BMW'::text) AND ((model_name)::text = '318i'::text) AND ((sales_state)::text = 'onsale'::text))
  Filter: has_auto_gear
  ->  Bitmap Index Scan on index_cars_onsale_on_brand_and_model_name  (cost=0.00..12.54 rows=585 width=0) (actual time=15.211..15.211 rows=7411 loops=1)"
        Index Cond: (((brand)::text = 'BMW'::text) AND ((model_name)::text = '318i'::text))
Total runtime: 56.851 ms
4b9b3361

Ответ 1

Хотя этот простой план не так полезен, http://explain.depesz.com действительно полезен. См. http://explain.depesz.com/s/t4fi. Обратите внимание на вкладку "Статистика" и раскрывающееся меню "Параметры".

Комментарии к этому плану:

  • Оценочный счетчик строк (183) достаточно сопоставим с фактическим количеством строк (25). Это не в сотни раз больше и не равно 1. Вас больше интересуют порядки величин, когда речь идет о оценках rowcount, или о проблемах "1 против не 1". (Вам даже не нужна "достаточно близкая для правильной работы" точность - "достаточно близко для ведения военных контрактов" ). Оценка избирательности и статистика кажутся разумными.

  • Он использует предоставленный двухстолбцовый индекс (index scan using index_cars_onsale_on_brand_and_model_name), поэтому он соответствует условию частичного индекса. Вы можете видеть это в Filter: has_auto_gear. Также показано условие поиска индекса.

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

Я не вижу здесь ничего плохого. Тем не менее, этот запрос, скорее всего, принесет пользу от сканирования только PostgreSQL 9.2.

Возможно, здесь есть таблица разрастания, но с учетом индекса с двумя столбцами и чистого количества строк время отклика не совсем необоснованно, особенно для таблицы с 170 (!!) столбцами, которые, вероятно, будут соответствовать относительно небольшим кортежи на каждую страницу. Если вы можете позволить себе некоторое время простоя, попробуйте VACUUM FULL реорганизовать таблицу и перестроить индекс. Это будет блокировать столик в течение некоторого времени, пока он восстанавливает его. Если вы не можете позволить себе время простоя, см. pg_reorg и/или CREATE INDEX CONCURRENTLY и ALTER INDEX ... RENAME TO.

Иногда вы можете найти EXPLAIN (ANALYZE, BUFFERS, VERBOSE) более информативным, поскольку он может отображать доступ к буферам и т.д.

Один из вариантов, который может сделать этот запрос быстрее (хотя он рискует немного замедлить другие запросы), заключается в разделении таблицы на brand и включении constraint_exclusion. См. partitioning.

Ответ 2

Ну... первое, что я могу вам сказать, это то, что ваша база данных ожидает (из статистики) получить 183 строки. На самом деле он получает 25 строк. Хотя это, вероятно, не слишком актуально в этом случае (т.е. С этими небольшими суммами и без тяжелых операций, не нужно беспокоиться об оценке его ошибочно).

Большая проблема (imho) заключается в том, что простой поиск индекса для 25 строк занимает 35 мс. Это кажется немного. Является ли база данных достаточно тяжелой, чтобы иметь хотя бы все индексы в памяти? Это не является чрезмерным, хотя, для меня это кажется немного медленным.

Что касается ваших объяснений, я бы рекомендовал использовать explain.depesz.com: http://explain.depesz.com/s/sA6