15.3. Параллельные планы#

15.3. Параллельные планы

15.3. Параллельные планы #

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

15.3.1. Параллельные сканирования #

Следующие типы параллельных сканирований таблиц в настоящее время поддерживаются.

  • В параллельном последовательном сканировании блоки таблицы будут разделены на диапазоны и распределены между взаимодействующими процессами. Каждый рабочий процесс завершит сканирование своего заданного диапазона блоков перед запросом дополнительного диапазона блоков.

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

  • В параллельном сканировании индекса или параллельном сканировании только индекса взаимодействующие процессы поочередно считывают данные из индекса. В настоящее время параллельное сканирование индексов поддерживается только для индексов btree. Каждый процесс будет занимать один индексный блок и сканировать и возвращать все кортежи, на которые ссылается этот блок; другие процессы могут в то же время возвращать кортежи из другого индексного блока. Результаты параллельного сканирования btree возвращаются в отсортированном порядке в каждом рабочем процессе.

Другие типы сканирования, такие как сканирование не-B-деревьев индексов, могут поддерживать параллельные сканирования в будущем.

15.3.2. Параллельные соединения #

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

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

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

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

15.3.3. Параллельная агрегация #

Tantor BE поддерживает параллельную агрегацию путем агрегации в два этапа. Сначала каждый процесс, участвующий в параллельной части запроса, выполняет этап агрегации, производя частичный результат для каждой группы, о которой этот процесс знает. Это отражается в плане как узел Partial Aggregate. Затем частичные результаты передаются лидеру через узлы Gather или Gather Merge. Наконец, лидер повторно агрегирует результаты по всем рабочим процессам, чтобы получить окончательный результат. Это отражается в плане как узел Finalize Aggregate.

Поскольку узел Finalize Aggregate выполняется на процессе-лидере, запросы, которые создают относительно большое количество групп по сравнению с количеством входных строк, будут выглядеть менее предпочтительными для планировщика запросов. Например, в худшем случае количество групп, видимых узлом Finalize Aggregate, может быть таким же, как количество входных строк, видимых всеми рабочими процессами на этапе Partial Aggregate. В таких случаях использование параллельной агрегации не принесет никакой выгоды в производительности. Планировщик запросов учитывает это при планировании и, вероятно, не выберет параллельную агрегацию в этом сценарии.

Все ситуации не поддерживают параллельную агрегацию. Каждая агрегация должна быть безопасной для параллельной обработки данных safe и иметь функцию комбинирования. Если агрегат имеет состояние перехода типа internal, он должен иметь функции сериализации и десериализации. См. CREATE AGGREGATE для получения дополнительной информации. Параллельная агрегация не поддерживается, если любой вызов агрегатной функции содержит ключевое слово DISTINCT или ORDER BY, а также не поддерживается для агрегатов с отсортированным набором или когда запрос включает GROUPING SETS. Она может использоваться только тогда, когда все соединения, участвующие в запросе, также являются частью параллельной обработки плана.

15.3.4. Параллельное объединение #

Всякий раз, когда Tantor BE должен объединить строки из нескольких источников в один набор результатов, он использует узел плана Append или MergeAppend. Это часто происходит при реализации операции UNION ALL или при сканировании секционированной таблицы. Такие узлы могут использоваться в параллельных планах так же, как и в любом другом плане. Однако, в параллельном плане планировщик может вместо этого использовать узел Parallel Append.

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

Также, в отличие от обычного узла Append, который может иметь только частичные дочерние планы при использовании в параллельном плане, узел Parallel Append может иметь как частичные, так и неполные дочерние планы. Неполные дочерние планы будут сканироваться только одним процессом, поскольку повторное сканирование приведет к дублированию результатов. Планы, которые включают объединение нескольких наборов результатов, могут поэтому достигать крупнозернистой параллелизма, даже если эффективные частичные планы недоступны. Например, рассмотрим запрос к секционированной таблице, который может быть реализован эффективно только с использованием индекса, не поддерживающего параллельное сканирование. Планировщик может выбрать Parallel Append из обычных планов Index Scan; каждое отдельное сканирование индекса должно быть выполнено до конца одним процессом, но разные сканирования могут выполняться одновременно разными процессами.

enable_parallel_append

15.3.5. Советы по параллельному планированию #

Если запрос, который ожидается, не создает параллельный план, вы можете попробовать уменьшить parallel_setup_cost или parallel_tuple_cost. Конечно, этот план может оказаться медленнее, чем последовательный план, который предпочтительно выбирает планировщик, но это не всегда так. Если вы не получаете параллельный план даже с очень малыми значениями этих параметров (например, после установки их обоих в ноль), может быть какая-то причина, по которой планировщик запросов не может сгенерировать параллельный план для вашего запроса. См. Раздел 15.2 и Раздел 15.4 для получения информации о причинах этого.

При выполнении параллельного плана вы можете использовать EXPLAIN (ANALYZE, VERBOSE) для отображения статистики для каждого узла плана. Это может быть полезно для определения равномерного распределения работы между всеми узлами плана и более общего понимания характеристик производительности плана.