Как понять EXPLAIN ANALYZE

Я не очень хорошо знаком с результатами 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. Моя БД имеет около 7,5 ГБ ОЗУ, таблица cars содержит 3,1 млн строк, а примерно 2,0 млн строк имеют 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;

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

РЕДАКТИРОВАТЬ:

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;

среднее: 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

person Niels Kristian    schedule 16.10.2012    source источник
comment
Непосредственно перед тем, как я выполнил запрос, я провел полную очистку и анализ... Мой веб-сайт представляет собой поисковую систему для подержанных автомобилей, поэтому время совершенно неприемлемо. Моя цель - сократить общее время до менее 1 секунды. Как вы думаете, это вообще возможно, или мне придется искать другую технологию, а не рациональную базу данных?   -  person Niels Kristian    schedule 16.10.2012
comment
@NielsKristian Я думаю, что большая часть проблемы может заключаться в части из 170 столбцов. Насколько большой стол? SELECT pg_relation_size('cars'), pg_total_relation_size('cars');. Также SELECT pg_relation_size('index_cars_onsale_on_brand_and_model_name'); для получения размера индекса. Какова средняя ширина ряда? SELECT avg(pg_column_size(cars)) FROM test cars limit 5000;   -  person Craig Ringer    schedule 16.10.2012
comment
Я не вижу его в сообщении выше, но мне интересно, есть ли у вас индекс has_auto_gear?   -  person jcern    schedule 16.10.2012
comment
@jcern Существует частичный индекс с условием фильтрации для has_auto_gear. То же самое, но быстрее для этой цели.   -  person Craig Ringer    schedule 16.10.2012
comment
Можете ли вы рассказать нам, что происходит, когда вы удаляете предложение LIMIT?   -  person Neville Kuyt    schedule 16.10.2012
comment
@CraigRinger Я обновил статистику в РЕДАКТИРОВКЕ выше... :-)   -  person Niels Kristian    schedule 16.10.2012
comment
@NevilleK Я обновил статистику выше   -  person Niels Kristian    schedule 16.10.2012
comment
Это таблица размером 4,5 ГБ, включая таблицы TOAST и индексы; 2 ГБ для необработанной таблицы без внешнего хранилища. Однако индекс крошечный, 44 МБ. Каждая строка в среднем имеет ширину 600 байт, что довольно много, но не безумно много. Я ожидал бы лучшей производительности, чем это. Мне было бы любопытно узнать, как это работает, если вы сбросите таблицу, загрузите ее в локальный экземпляр PostgreSQL на полуприличном компьютере и протестируете ее там.   -  person Craig Ringer    schedule 16.10.2012


Ответы (2)


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

На что обратить внимание в этом плане:

  • Предполагаемое количество строк (183) вполне сопоставимо с фактическим количеством строк (25). Это не в сотни раз больше и не в 1. Вас больше интересуют порядки, когда речь идет об оценках количества строк или проблемах «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. См. раздел разделы.

person Craig Ringer    schedule 16.10.2012
comment
Привет, спасибо за ваши объяснения. Незадолго до того, как я запустил запрос, я сделал полную очистку - person Niels Kristian; 16.10.2012
comment
@NielsKristian Рассмотрите возможность разделения, если ничего не помогает; см. редактировать, чтобы ответить. Кроме того, рассмотрите возможность редактирования вопроса, чтобы отобразить EXPLAIN (ANALYZE, BUFFERS, VERBOSE) результатов. - person Craig Ringer; 16.10.2012
comment
Спасибо за огромную помощь! Я, конечно, получил гораздо больше знаний об отладке SQL. Решение получается уменьшить количество столбцов в таблице, и быть более точным в том, что я выбираю. Во-вторых, я сделал несколько более подходящих индексов. - person Niels Kristian; 16.10.2012
comment
Буду иметь в виду эту важную информацию. достаточно близко для учета военных контрактов. - person B Seven; 05.07.2018
comment
@CraigRinger спасибо за подробное объяснение. Не могли бы вы также добавить немного о значениях значений cost? Мне они кажутся произвольными, просто высокое — плохо, низкое — хорошо. - person Todd; 12.09.2018
comment
@ Тодд В значительной степени. Оценки представляют собой несколько произвольные затраты в абстрактных единицах, основанные на количестве ожидаемых случайных выборок страниц, последовательных выборок страниц ввода-вывода, кортежей, обрабатываемых в памяти, и т. д. - person Craig Ringer; 12.09.2018

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

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

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

person Wolph    schedule 16.10.2012
comment
@NielsKristian Мне кажется, миллисекунды. Полагаю, вы читаете его в нотации стиля DE, например, 123.456.789,50 вместо 123 456 789,50 в стиле США/Австралии/Великобритании. AFAIK Pg не локализует время в EXPLAIN ANALYZE, поэтому оно должно быть 35 миллисекунд. - person Craig Ringer; 16.10.2012