MySQL объясняет вывод довольно простым. PostgreSQL немного сложнее. Я тоже не смог найти хороший ресурс, который объясняет это.
Можете ли вы описать, что именно объясняет, говорит или, по крайней мере, указывает мне в сторону хорошего ресурса?
MySQL объясняет вывод довольно простым. PostgreSQL немного сложнее. Я тоже не смог найти хороший ресурс, который объясняет это.
Можете ли вы описать, что именно объясняет, говорит или, по крайней мере, указывает мне в сторону хорошего ресурса?
Explaining_EXPLAIN.pdf тоже может помочь.
Он выполняется от большинства отступов до наименьших отступов, и я верю в нижней части плана вверх. (Итак, если есть два раздела с отступом, сначала выполняется первый шаг вниз по странице, а затем, когда они встречаются с другим исполнением, тогда выполняется объединение, соединяющее их.)
Идея заключается в том, что на каждом шаге есть 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
, затем сортирует этот набор данных с помощью quicksort (используемый сортировка изменится, если данные не подходят в ОЗУ), затем устанавливает это в сторону.
Затем он сканирует orditematt_attributeid_idx
, чтобы найти нужные строки из orderitemattribute
, а затем сортирует этот набор данных с помощью быстрой сортировки.
Затем он берет два набора данных и объединяет их. (Объединение слияния - это своего рода операция "запирания", когда он параллельно перемещает два отсортированных набора данных, испуская объединенную строку, когда они совпадают.)
Как я уже сказал, вы работаете через внутреннюю часть плана с внешней частью, снизу вверх.
Часть, которую я всегда считала запутанной, - это затраты на запуск и общую стоимость. Я Google это каждый раз, когда я забываю об этом, что возвращает меня сюда, что не объясняет разницу, поэтому я пишу этот ответ. Это то, что я почерпнул из Postgres EXPLAIN
documentation, объяснил, насколько я понимаю.
Вот пример из приложения, которое управляет форумом:
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:
(Когда вы используете PgAdmin, вы можете направить указатель мыши на компонент, чтобы прочитать данные о стоимости.)
Стоимость представлена как кортеж, например. стоимость LIMIT
равна cost=0.00..3.39
, а стоимость последовательного сканирования post
равна cost=0.00..15629.12
. Первое число в кортеже - это стоимость запуска, а второе - общая стоимость. Поскольку я использовал EXPLAIN
, а не EXPLAIN ANALYZE
, эти затраты представляют собой оценки, а не фактические меры.
В качестве осложнения каждая "родительская" стоимость node включает в себя стоимость его дочерних узлов. В текстовом представлении дерево представлено отступом, например. LIMIT
является родительским node и Seq Scan
является его дочерним. В представлении PgAdmin стрелки указывают от дочернего к родительскому - направление потока данных - что может быть неточным, если вы знакомы с теорией графов.
В документации говорится, что затраты включают все дочерние узлы, но обратите внимание, что общая стоимость родительского 3.39
намного меньше общей стоимости его дочернего 15629.12
. Общая стоимость не включена, потому что компоненту, например LIMIT
, не нужно обрабатывать весь его ввод. См. Пример EXPLAIN SELECT * FROM tenk1 WHERE unique1 < 100 AND unique2 > 9000 LIMIT 2;
в Postgres EXPLAIN
documentation.
В приведенном выше примере время запуска для обоих компонентов равно нулю, поскольку ни один из компонентов не должен выполнять какую-либо обработку до того, как он начнет записывать строки: последовательное сканирование считывает первую строку таблицы и испускает ее. 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)
И графически:
Опять же, последовательное сканирование на post
не имеет стоимости запуска: он сразу же выводит строки. Но сортировка имеет значительную стоимость запуска 23283.24
, потому что она должна сортировать всю таблицу до того, как она выведет хотя бы одну строку. Общая стоимость сортировки 23859.27
немного выше, чем стоимость запуска, что отражает тот факт, что как только весь набор данных был отсортирован, отсортированные данные могут быть испусканы очень быстро.
Обратите внимание, что время запуска LIMIT
23283.24
точно совпадает с временем запуска сортировки. Это связано не только с тем, что LIMIT
имеет большое время запуска. Фактически у него фактически есть нулевое время запуска, но EXPLAIN
сворачивает все дочерние затраты для каждого родителя, поэтому время запуска LIMIT
включает в себя время запуска его дочерних элементов.
Этот свод затрат может затруднить понимание стоимости выполнения каждого отдельного компонента. Например, наш LIMIT
имеет нулевое время запуска, но это не очевидно на первый взгляд. По этой причине несколько других людей связаны с explain.depesz.com, инструментом, созданным Hubert Lubaczewski (aka depesz), который помогает понять EXPLAIN
by - среди прочего, - вычесть издержки на ребенка от материальных затрат. Он упоминает некоторые другие сложности в коротком сообщении в блоге о своем инструменте.
Также имеется интерактивный вспомогательный инструмент, Depesz, в котором указывается, где стоят дорогие части результатов анализа.
также имеет один, здесь те же результаты, которые мне дают более четкое описание проблемы.
PgAdmin покажет вам графическое представление плана объяснения. Переключение между ними может помочь вам понять, что означает текстовое представление. Однако, если вы просто хотите знать, что он собирается делать, вы можете всегда использовать графический интерфейс.
Официальная документация PostgreSQL содержит интересное, подробное объяснение того, как понимать вывод объяснения.
Если вы устанавливаете pgadmin, есть кнопка Объяснения, которая, а также вывод текста, рисует диаграммы того, что происходит, показывая фильтры, сортировки и подмножества, которые мне кажутся полезными, чтобы увидеть, что происходит.