Что мне объясняет PostgreSQL? - PullRequest
       37

Что мне объясняет PostgreSQL?

98 голосов
/ 23 сентября 2008

Вывод объяснения MySQL довольно прост. PostgreSQL немного сложнее. Мне также не удалось найти хороший ресурс, который бы это объяснял.

Можете ли вы описать, что именно объясняет, или, по крайней мере, указать мне направление на хороший ресурс?

Ответы [ 7 ]

51 голосов
/ 23 сентября 2008

Explaining_EXPLAIN.pdf тоже может помочь.

45 голосов
/ 19 февраля 2016

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

Вот пример из приложения, которое управляет форумом:

EXPLAIN SELECT * FROM post LIMIT 50;

Limit  (cost=0.00..3.39 rows=50 width=422)
  ->  Seq Scan on post  (cost=0.00..15629.12 rows=230412 width=422)

Вот графическое объяснение от PgAdmin:

graphical explanation of first query

(Когда вы используете PgAdmin, вы можете навести указатель мыши на компонент, чтобы прочитать сведения о стоимости.)

Стоимость представляется в виде кортежа, например, стоимость LIMIT составляет cost=0.00..3.39, а стоимость последовательного сканирования post составляет cost=0.00..15629.12. Первое число в кортеже - это начальная стоимость , а второе число - общая стоимость . Поскольку я использовал EXPLAIN, а не EXPLAIN ANALYZE, эти затраты являются оценочными, а не фактическими.

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

Как сложность, затраты каждого "родительского" узла включают в себя стоимость его дочерних узлов. В текстовом представлении дерево представлено отступом, например, LIMIT является родительским узлом, а Seq Scan является его дочерним узлом. В представлении PgAdmin стрелки указывают от дочернего элемента к родительскому - направление потока данных - что может быть нелогичным, если вы знакомы с теорией графов.

В документации сказано, что в стоимость включены все дочерние узлы, но обратите внимание, что общая стоимость родительского 3.39 намного меньше, чем общая стоимость его дочернего 15629.12. Общая стоимость не включена, потому что компоненту типа LIMIT не нужно обрабатывать весь ввод. См. Пример EXPLAIN SELECT * FROM tenk1 WHERE unique1 < 100 AND unique2 > 9000 LIMIT 2; в Postgres EXPLAIN документации .

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

Когда компонент должен будет выполнить большую обработку, прежде чем он сможет начать выводить какие-либо строки? Есть много возможных причин, но давайте посмотрим на один четкий пример. Вот тот же запрос из ранее, но теперь содержащий предложение ORDER BY:

EXPLAIN SELECT * FROM post ORDER BY body LIMIT 50;

Limit  (cost=23283.24..23283.37 rows=50 width=422)
  ->  Sort  (cost=23283.24..23859.27 rows=230412 width=422)
        Sort Key: body
        ->  Seq Scan on post  (cost=0.00..15629.12 rows=230412 width=422)

И графически:

graphical explanation of second query

Еще раз, последовательное сканирование на post не требует затрат при запуске: оно сразу начинает выводить строки. Но такая сортировка требует значительных затрат на запуск 23283.24, поскольку ей необходимо отсортировать всю таблицу, прежде чем она сможет вывести хотя бы одну строку . Общая стоимость сортировки 23859.27 лишь немного выше стоимости запуска, что отражает тот факт, что после сортировки всего набора данных отсортированные данные могут быть отправлены очень быстро.

Обратите внимание, что время запуска LIMIT 23283.24 точно равно времени запуска сортировки. Это не потому, что само по себе LIMIT имеет большое время запуска. Фактически он сам по себе имеет нулевое время запуска, но EXPLAIN сворачивает все дочерние затраты для каждого родителя, поэтому время запуска LIMIT включает сумму времени запуска его дочерних элементов.

Такое накопление затрат может затруднить понимание стоимости выполнения каждого отдельного компонента. Например, у нашего LIMIT нулевое время запуска, но это не очевидно с первого взгляда. По этой причине несколько других людей связались с объяснить.depesz.com , инструментом, созданным Юбертом Любачевским (он же depesz), который помогает понять EXPLAIN, среди прочего - путем вычитания дочерних расходов из родительских расходов , Он упоминает о некоторых других сложностях в коротком блоге о своем инструменте.

40 голосов
/ 23 сентября 2008

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

Идея состоит в том, что на каждом шаге есть 1 или 2 набора данных, которые поступают и обрабатываются по какому-либо правилу. Если только один набор данных, эта операция выполняется с этим набором данных. (Например, просканируйте индекс, чтобы выяснить, какие строки вы хотите, отфильтруйте набор данных или отсортируйте его.) Если два, два набора данных - это две вещи, которые имеют отступ с последующим отступом, и к ним присоединяется правило, которое вы видите. Смысл большинства правил можно довольно легко угадать (особенно, если вы уже читали кучу планов объяснений ранее), однако вы можете попытаться проверить отдельные элементы, посмотрев документацию или (проще), просто бросив фразу в Google вместе с несколькими ключевыми словами, такими как EXPLAIN.

Это, очевидно, не полное объяснение, но оно предоставляет достаточно контекста, чтобы вы обычно могли понять, что вы хотите. Например, рассмотрите этот план из фактической базы данных:

explain analyze
select a.attributeid, a.attributevalue, b.productid
from orderitemattribute a, orderitem b
where a.orderid = b.orderid
and a.attributeid = 'display-album'
and b.productid = 'ModernBook';

------------------------------------------------------------------------------------------------------------------------------------------------------------

 Merge Join  (cost=125379.14..125775.12 rows=3311 width=29) (actual time=841.478..841.478 rows=0 loops=1)
   Merge Cond: (a.orderid = b.orderid)
   ->  Sort  (cost=109737.32..109881.89 rows=57828 width=23) (actual time=736.163..774.475 rows=16815 loops=1)
         Sort Key: a.orderid
         Sort Method:  quicksort  Memory: 1695kB
         ->  Bitmap Heap Scan on orderitemattribute a  (cost=1286.88..105163.27 rows=57828 width=23) (actual time=41.536..612.731 rows=16815 loops=1)
               Recheck Cond: ((attributeid)::text = 'display-album'::text)
               ->  Bitmap Index Scan on (cost=0.00..1272.43 rows=57828 width=0) (actual time=25.033..25.033 rows=16815 loops=1)
                     Index Cond: ((attributeid)::text = 'display-album'::text)
   ->  Sort  (cost=15641.81..15678.73 rows=14769 width=14) (actual time=14.471..16.898 rows=1109 loops=1)
         Sort Key: b.orderid
         Sort Method:  quicksort  Memory: 76kB
         ->  Bitmap Heap Scan on orderitem b  (cost=310.96..14619.03 rows=14769 width=14) (actual time=1.865..8.480 rows=1114 loops=1)
               Recheck Cond: ((productid)::text = 'ModernBook'::text)
               ->  Bitmap Index Scan on id_orderitem_productid  (cost=0.00..307.27 rows=14769 width=0) (actual time=1.431..1.431 rows=1114 loops=1)
                     Index Cond: ((productid)::text = 'ModernBook'::text)
 Total runtime: 842.134 ms
(17 rows)

Попробуйте прочитать это сами и посмотрите, имеет ли это смысл.

Что я прочитал, так это то, что база данных сначала сканирует индекс id_orderitem_productid, используя его для поиска нужных строк из orderitem, а затем сортирует этот набор данных с помощью быстрой сортировки (используемая сортировка изменится, если данные не будут соответствовать в оперативной памяти), затем откладывает это.

Затем он сканирует orditematt_attributeid_idx, чтобы найти нужные строки из orderitemattribute, а затем сортирует этот набор данных с помощью быстрой сортировки.

Затем он берет два набора данных и объединяет их. (Объединение слиянием - это своего рода операция «архивирования», при которой два отсортированных набора данных проходят параллельно, испуская объединенную строку, когда они совпадают.)

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

21 голосов
/ 24 сентября 2008

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

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

13 голосов
/ 23 сентября 2008

PgAdmin покажет вам графическое представление плана объяснения. Переключение между ними может действительно помочь вам понять, что означает текстовое представление. Тем не менее, если вы просто хотите знать, что он собирается сделать, вы можете всегда использовать GUI.

6 голосов
/ 18 марта 2013

Официальная документация PostgreSQL предоставляет интересное, подробное объяснение того, как понимать вывод объяснения.

0 голосов
/ 25 сентября 2008

Если вы устанавливаете pgadmin, есть кнопка «Объяснить», которая, наряду с выводом текста, рисует диаграммы того, что происходит, показывая фильтры, сортировки и объединения подмножеств, которые я считаю действительно полезными, чтобы увидеть, что происходит.

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