15.3. Параллельные планы
Так как каждый рабочий процесс выполняет параллельную часть плана до конца, нельзя просто взять обычный план запроса и запустить его в нескольких исполнителях. В этом случае все исполнители выдавали бы полные копии выходного набора результатов, так что запрос выполнится не быстрее, чем обычно, а его результаты могут быть некорректными. Вместо этого параллельной частью плана должно быть то, что для оптимизатора представляется как частичный план; то есть такой план, при выполнении которого в отдельном процессе будет получено только подмножество выходных строк, а каждая требующаяся строка результата будет гарантированно выдана ровно одним из сотрудничающих процессов.
15.3.1. Параллельные сканирования
В настоящее время единственным вариантом сканирования, адаптированным для работы в параллельном режиме, является последовательное сканирование. Таким образом, целевая таблица в параллельном плане всегда будет сканироваться узлом Parallel Seq Scan
. Блоки отношения разделяются между сотрудничающими процессами и выдаются им по одному, так что доступ к отношению остаётся последовательным. Каждый отдельный процесс сначала посещает все кортежи на назначенной ему странице, и только затем переходит к новой.
15.3.2. Параллельные соединения
Целевая таблица может соединяться с одной или несколькими другими таблицами с использованием вложенных циклов или соединений по хешу. Внутренней стороной соединения может быть любой вид не параллельного плана, который в остальном поддерживается планировщиком, при условии, что он безопасен для выполнения в параллельном исполнителе. Например, это может быть сканирование индекса, при котором находится значение, взятое из внешней стороны соединения. Каждый рабочий процесс будет выполнять внутреннюю сторону соединения в полном объёме, что для соединения по хешу означает, что в каждом рабочем процессе будет строиться одна и та же хеш-таблица.
15.3.3. Параллельное агрегирование
Postgres Pro поддерживает параллельное агрегирование, выполняя агрегирование в два этапа. Сначала каждый процесс, задействованный в параллельной части запроса, выполняет шаг агрегирования, выдавая частичный результат для каждой известной ему группы. В плане это отражает узел Partial Aggregate
. Затем эти промежуточные результаты передаются ведущему через узел Gather
. И наконец, ведущий заново агрегирует результаты всех рабочих процессов, чтобы получить окончательный результат. Это отражает в плане узел Finalize Aggregate
.
Так как узел Finalize Aggregate
выполняется в ведущем процессе, запросы, выдающие достаточно большое количество групп по отношению к числу входных строк, будут расцениваться планировщиком как менее предпочтительные. Например, в худшем случае количество групп, выявленных узлом Finalize Aggregate
, может равняться числу входных строк, обработанных всеми рабочими процессами на этапе Partial Aggregate
. Очевидно, что в такой ситуации использование параллельного агрегирования не даст никакого выигрыша производительности. Планировщик запросов учитывает это в процессе планирования, так что выбор параллельного агрегирования в подобных случаях очень маловероятен.
Параллельное агрегирование поддерживается не во всех случаях. Чтобы оно поддерживалось, агрегатная функция должна быть безопасной для распараллеливания и должна иметь комбинирующую функцию. Если переходное состояние агрегатной функции имеет тип internal
, она должна также иметь функции сериализации и десериализации. За подробностями обратитесь к CREATE AGGREGATE. Параллельное агрегирование не поддерживается, если вызов агрегатной функции содержит предложение DISTINCT
или ORDER BY
. Также оно не поддерживается для сортирующих агрегатов или когда запрос включает предложение GROUPING SETS
. Оно может использоваться только когда все соединения, задействованные в запросе, также входят в параллельную часть плана.
15.3.4. Советы по параллельным планам
Если для запроса ожидается параллельный план, но такой план не строится, можно попытаться уменьшить parallel_setup_cost или parallel_tuple_cost. Разумеется, этот план может оказаться медленнее последовательного плана, предпочитаемого планировщиком, но не всегда. Если вы не получаете параллельный план даже с очень маленькими значениями этих параметров (например, сбросив оба их в ноль), может быть какая-то веская причина тому, что планировщик запросов не может построить параллельный план для вашего запроса. За информацией о возможных причинах обратитесь к Разделу 15.2 и Разделу 15.4.
Когда выполняется параллельный план, вы можете применить EXPLAIN (ANALYZE, VERBOSE)
, чтобы просмотреть статистику по каждому узлу плана в разрезе рабочих процессов. Это может помочь определить, равномерно ли распределяется работа между всеми узлами плана, и на более общем уровне понимать характеристики производительности плана.