15.1. Как работает параллельный запрос#
15.1. Как работает параллельный запрос #
Когда оптимизатор определяет, что параллельный запрос является наиболее быстрой стратегией выполнения для конкретного запроса, он создает план запроса, который включает узел Gather или Gather Merge. Вот простой пример:
EXPLAIN SELECT * FROM pgbench_accounts WHERE filler LIKE '%x%'; QUERY PLAN ------------------------------------------------------------------------------------- Gather (cost=1000.00..217018.43 rows=1 width=97) Workers Planned: 2 -> Parallel Seq Scan on pgbench_accounts (cost=0.00..216018.33 rows=1 width=97) Filter: (filler ~~ '%x%'::text) (4 rows)
Во всех случаях у узла Gather
или Gather Merge
будет ровно один дочерний план, который будет выполняться параллельно. Если узел Gather
или Gather Merge
находится в самом верху дерева плана, то весь запрос будет выполняться параллельно. Если он находится где-то еще в дереве плана, то только часть плана ниже него будет выполняться параллельно. В приведенном выше примере запрос обращается только к одной таблице, поэтому, помимо самого узла Gather
, есть только один узел плана; так как этот узел плана является дочерним узлом узла Gather
, он будет выполняться параллельно.
С помощью EXPLAIN можно узнать количество выбранных планировщиком рабочих процессов. Когда во время выполнения запроса достигается узел Gather
, процесс, реализующий сессию пользователя, запросит определенное количество фоновых рабочих процессов, равное количеству выбранных планировщиком рабочих процессов. Количество фоновых рабочих процессов, которые планировщик будет рассматривать для использования, ограничено максимумом, заданным параметром max_parallel_workers_per_gather. Общее количество фоновых рабочих процессов, которые могут существовать одновременно, ограничено как параметром max_worker_processes, так и параметром max_parallel_workers. Поэтому параллельный запрос может выполняться с меньшим количеством рабочих процессов, чем было запланировано, или даже без рабочих процессов вообще. Оптимальный план может зависеть от количества доступных рабочих процессов, поэтому это может привести к плохой производительности запроса. Если такая ситуация происходит часто, рассмотрите возможность увеличения параметров max_worker_processes
и max_parallel_workers
, чтобы одновременно запускать больше рабочих процессов, или уменьшения параметра max_parallel_workers_per_gather
, чтобы планировщик запрашивал меньшее количество рабочих процессов.
Каждый фоновый рабочий процесс, успешно запущенный для заданного параллельного запроса, будет выполнять параллельную часть плана. Лидер также будет выполнять эту часть плана, но у него есть дополнительная ответственность: он должен также читать все кортежи, сгенерированные рабочими процессами. Когда параллельная часть плана генерирует только небольшое количество кортежей, лидер часто будет вести себя очень похоже на дополнительный рабочий процесс, ускоряя выполнение запроса. Напротив, когда параллельная часть плана генерирует большое количество кортежей, лидер может быть почти полностью занят чтением кортежей, сгенерированных рабочими процессами, и выполнением любых дополнительных шагов обработки, которые требуются узлам плана выше уровня узла Gather
или узла Gather Merge
. В таких случаях лидер будет выполнять очень мало работы по выполнению параллельной части плана.
Когда узел в верхней части параллельной части плана имеет Gather Merge
вместо Gather
, это указывает на то, что каждый процесс, выполняющий параллельную часть плана, производит кортежи в отсортированном порядке, и что лидер выполняет слияние с сохранением порядка. В отличие от этого, Gather
читает кортежи с рабочих процессов в любом удобном порядке, разрушая любой существовавший порядок сортировки.