Доступ к данным
Параллельный доступ
16
Авторские права
© Postgres Professional, 2019–2024
Авторы: Егор Рогов, Павел Лузанов, Павел Толмачев, Илья Баштанов
Фото: Олег Бартунов (монастырь Пху и пик Бхрикути, Непал)
Использование материалов курса
Некоммерческое использование материалов курса (презентации,
демонстрации) разрешается без ограничений. Коммерческое
использование возможно только с письменного разрешения компании
Postgres Professional. Запрещается внесение изменений в материалы
курса.
Обратная связь
Отзывы, замечания и предложения направляйте по адресу:
Отказ от ответственности
Компания Postgres Professional не несет никакой ответственности за
любые повреждения и убытки, включая потерю дохода, нанесенные
прямым или непрямым, специальным или случайным использованием
материалов курса. Компания Postgres Professional не предоставляет
каких-либо гарантий на материалы курса. Материалы курса
предоставляются на основе принципа «как есть» и компания Postgres
Professional не обязана предоставлять сопровождение, поддержку,
обновления, расширения и изменения.
2
Темы
Параллельные планы
Размер пула процессов
Параллельное последовательное сканирование
Параллельный индексный доступ
3
Параллельные планы
Ведущий процесс
выполняет последовательную часть плана
запускает рабочие процессы и получает от них данные
Рабочие процессы
одновременно работают над параллельной частью плана
Gather
параллельный
план
параллельный
план
параллельный
план
ведущий процесс
рабочий процесс рабочий процесс
подключается,
если нет другой
работы
последовательный
план
PostgreSQL поддерживает параллельное выполнение запросов. Идея
состоит в том, что ведущий процесс, выполняющий запрос, порождает
(с помощью postmaster, конечно) несколько рабочих процессов,
которые одновременно выполняют одну и ту же «параллельную» часть
плана. Результаты этого выполнения передаются ведущему процессу,
который собирает их в узле Gather.
Если рабочие процессы не успевают загрузить ведущего данными,
ведущий процесс тоже подключается к выполнению того же самого
параллельного плана.
Разумеется, запуск процессов и пересылка данных требуют
определенных ресурсов, поэтому далеко не каждый запрос
выполняется параллельно.
Кроме того, даже при параллельном выполнении не все шаги плана
запроса могут быть распараллелены. Часть операций может
выполняться ведущим процессом в одиночку, последовательно.
Заметим, что в PostgreSQL нет другого теоретически возможного
режима распараллеливания, при котором несколько процессов
составляют конвейер для обработки данных (грубо говоря, отдельные
узлы плана выполняются отдельными процессами). Разработчики
PostgreSQL сочли такой режим неэффективным.
4
Размер пула процессов
Gather Gather
max_parallel_workers_per_gather = 2
max_parallel_workers = 8
max_worker_processes = 8
рабочие процессы
параллельное выполнение запросов другие задачи
Параллельным выполнением управляет довольно много параметров.
Сначала рассмотрим те, что ограничивают число рабочих процессов.
Вообще механизм рабочих процессов используется не только для
параллельного выполнения запросов. Например, рабочие процессы
задействованы в логической репликации, ими могут пользоваться
расширения. Рабочие процессы можно использовать в прикладном
коде (подробнее об этом — в теме «Фоновые процессы» курса DEV2).
Общее число одновременно выполняющихся рабочих процессов
ограничено параметром max_worker_processes (по умолчанию 8).
Число одновременно выполняющихся рабочих процессов,
занимающихся параллельными планами, ограничено параметром
max_parallel_workers (по умолчанию тоже 8).
Число одновременно выполняющихся рабочих процессов,
обслуживающих один ведущий процесс, ограничено параметром
max_parallel_workers_per_gather (по умолчанию 2).
Значения этих параметров следует изменить в зависимости от
возможностей аппаратуры, объема данных и загруженности системы.
Например, даже если в базе данных есть большие таблицы и запросы
могли бы выиграть от распараллеливания, но при этом в системе нет
свободных ядер, то параллельное выполнение не будет иметь смысла.
5
Parallel Seq Scan
Параллельное последовательное сканирование
агрегация
параллельная агрегация
Число рабочих процессов
Ограничения
6
Parallel Seq Scan
4 3 5 2 1 10 12 8 7 11 4 9 6
Gather
Partial Aggregate
Partial Aggregate Partial Aggregate
страницы читаются последовательно, но разными процессами
Parallel Seq Scan
Parallel Seq Scan Parallel Seq Scan
N
U
L
L
Finalize Aggregate
Partial AggregatePartial Aggregate
Примером узла, выполняющегося в параллельном режиме является
Parallel Seq Scan — «параллельное последовательное сканирование».
Название звучит противоречиво, но, тем не менее, отражает суть
операции. Страницы таблицы читаются последовательно, в том же
самом порядке, в котором они читались бы при обычном
последовательном сканировании. Однако запросы на чтение выдаются
несколькими параллельно работающими процессами. Процессы
синхронизируются между собой, чтобы их запросы шли в правильном
порядке.
Преимущество такого сканирования проявляется в том, что
параллельные процессы одновременно обрабатывают свои страницы.
Чтобы эффект оправдал дополнительные накладные расходы на
пересылку данных от процесса к процессу, обработка должна быть
достаточно ресурсоемкой. Хорошим примером является агрегация
данных, поскольку для нее необходимы ресурсы процессора,
а пересылать требуется только одно итоговое число. В таком случае
параллельное выполнение запроса может занять существенно меньше
времени, чем последовательное.
8
Число рабочих процессов
Равно нулю (параллельный план не строится)
если размер таблицы < min_parallel_table_scan_size = 8MB
Фиксировано
если для таблицы указан параметр хранения parallel_workers
Вычисляется по формуле
1 + log
3
(размер таблицы / min_parallel_table_scan_size)
Всегда не больше max_parallel_workers_per_gather
Сколько рабочих процессов будет использоваться?
Планировщик вообще не будет рассматривать параллельное
сканирование, если физический размер таблицы меньше значения
параметра min_parallel_table_scan_size.
Обычно число процессов вычисляется по формуле, приведенной на
слайде. Она означает, что при увеличении таблицы в три раза будет
добавляться очередной процесс. Например, для стандартного значения
min_parallel_table_scan_size = 8MB:
таблица процессы таблица процессы
8MB 1 216MB 4
24MB 2 648MB 5
72MB 3 1.9GB 6
Число процессов можно и явно указать в параметре хранения
parallel_workers таблицы.
При этом число процессов в любом случае не будет превышать
значения параметра max_parallel_workers_per_gather. Если при
выполнении запроса доступное число процессов окажется меньше
запланированного, будут использоваться только доступные (вплоть
до последовательного выполнения, если пул полностью исчерпан).
10
Не распараллеливаются
Запросы на запись
а также запросы с блокировкой строк
Курсоры
в том числе запросы в цикле FOR в PL/pgSQL
Запросы с функциями PARALLEL UNSAFE
Запросы в функциях,
вызванных из распараллеленного запроса
Не каждый запрос может выполняться в параллельном режиме.
Не распараллеливаются запросы, изменяющие или блокирующие
данные (UPDATE, DELETE, SELECT FOR UPDATE и т. п.).
Не распараллеливаются запросы, выполнение которых может быть
приостановлено — это относится к запросам в курсорах, в том числе
в циклах FOR PL/pgSQL.
Не распараллеливаются запросы, содержащие функции, помеченные
как PARALLEL UNSAFE (пометки параллельности рассматриваются
в теме «Функции»).
Не распараллеливаются запросы, содержащиеся в функциях, которые
вызываются из распараллеленного запроса (чтобы не допустить
рекурсивного разрастания).
Часть из этих ограничений может быть снята в следующих версиях
PostgreSQL.
11
Только последовательно
Чтение результатов общих табличных выражений (CTE)
Чтение результатов нераскрываемых подзапросов
Обращения к временным таблицам
Вызовы функций PARALLEL RESTRICTED
Функции, использующие вложенные транзакции
В целом, чем большую часть плана удается выполнить параллельно,
тем больший возможен эффект. Однако есть ряд операций, которые
не препятствуют распараллеливанию плана, но сами могут
выполняться только последовательно в ведущем процессе.
К ним относятся:
чтение результатов общих табличных выражений (подзапросов
в предложении WITH);
чтение результатов других нераскрываемых подзапросов (которые
представляются в плане узлами, например, SubPlan);
обращения ко временным таблицам (так как они доступны только
ведущему процессу);
вызовы функций, помеченных как PARALLEL RESTRICTED.
Если в запросе вызывается функция, использующая вложенные
транзакции (например, функция на PL/pgSQL с обработкой
исключений), запрос завершится с ошибкой. Такие функции должны
быть помечены как PARALLEL RESTRICTED. Подробнее пометки
параллельности рассматриваются в теме «Функции».
13
Parallel Index Scan
Параллельное сканирование индекса
Параллельное сканирование только индекса
Параллельное сканирование по битовой карте
Число рабочих процессов
14
Parallel Index Scan
3 5 2 1 10 12 8 7 11 4 9 6
1 9
1 3 6 9 12
1 2 3 4 5 6 7 8 9 10 11 12
4
N
U
L
L
N
спуск к листу
выполняет один
процесс
Индексный доступ тоже может выполняться в параллельном режиме.
Это происходит в два этапа. Сначала ведущий процесс спускается от
корня дерева к листовой странице.
15
Parallel Index Scan
3 5 2 1 10 12 8 7 11 4 9 6
1 9
1 3 6 9 12
1 2 3 4 5 6 7 8 9 10 11 12
4
N
U
L
L
N
Затем рабочие процессы выполняют параллельное чтение листовых
страниц индекса, двигаясь по списку.
Процесс, прочитавший индексную страницу, выполняет и чтение
необходимых табличных страниц. При этом может получиться так, что
одну и ту же табличную страницу прочитают несколько процессов тот
пример показан на слайде: последняя табличная страница содержат
строки, ссылки на которые ведут от нескольких индексных страниц,
прочитанных разными процессами). Конечно, сама страница будет
находиться в буферном кеше в одном экземпляре.
17
Parallel Index Only Scan
4 3 5 2 1 10 12 8 7 11 4 9 6
1 9
1 3 6 9 12
1 2 3 4 5 6 7 8 9 10 11 12
N
U
L
L
N
страница есть
в карте видимости,
проверка не нужна
страницы нет
в карте видимости,
проверка по таблице
Сканирование только индекса тоже может выполняться параллельно.
Это происходит точно так же, как и при обычном индексном
сканировании: ведущий процесс спускается от корня к листовой
странице, а затем рабочие процессы параллельно сканируют листовые
страницы индекса, обращаясь при необходимости к соответствующим
страницам таблицы для проверки видимости.
19
Parallel Bitmap Heap Scan
3 5 2 1 10 12 8 7 11 4 9 6
1 9
1 3 6 9 12
1 2 3 4 5 6 7 8 9 10 11 12
4
N
U
L
L
N
Сканирование по битовой карте может выполняться параллельно.
Первый этап — сканирование индекса и построение битовой карты —
всегда выполняется последовательно ведущим процессом.
А второй этап — сканирование таблицы — выполняется рабочими
процессами параллельно. Это происходит аналогично параллельному
последовательному сканированию.
21
Число рабочих процессов
Равно нулю (параллельный план не строится)
если размер выборки < min_parallel_index_scan_size = 512kB
Фиксировано
если для таблицы указан параметр хранения parallel_workers
Вычисляется по формуле
1 + log
3
(размер выборки / min_parallel_index_scan_size)
Но не больше, чем max_parallel_workers_per_gather
Число рабочих процессов выбирается примерно так же, как и в случае
последовательного сканирования. Объем данных, который
предполагается прочитать из индекса (определяемый числом
индексных страниц), сравнивается со значением параметра
min_parallel_index_scan_size (по умолчанию 512kB).
Если в случае последовательного сканирования таблицы объем данных
определялся размером всей таблицы, то при индексном доступе
планировщик должен спрогнозировать, сколько индексных страниц
будет прочитано. Как именно это происходит, рассматривается позже
в теме «Базовая статистика».
Если размер выборки меньше, параллельный план не рассматривается
оптимизатором. Например, никогда не будет выполняться параллельно
доступ к одному значению — в этом случае просто нечего
распараллеливать.
Если размер выборки достаточно велик, число рабочих процессов
определяется по формуле, если только оно не указано явно
в параметре хранения parallel_workers таблицы (не индекса!).
Число процессов в любом случае не будет превышать значения
параметра max_parallel_workers_per_gather.
22
Итоги
Параллельные операции используют ресурсы процессора
в нескольких рабочих процессах
PostgreSQL выделяет рабочие процессы из общего пула
Все методы доступа могут работать в параллельном режиме
23
Практика
Выполните запрос, вычисляющий общее количество
бронирований, с различными настройками планировщика
и сравните планы выполнения:
1. С настройками по умолчанию.
2. Запретив последовательное сканирование.
3. Дополнительно запретив сканирование только индекса.
4. Разрешив все методы доступа, но отключив параллельность.
1. Речь идет о запросе
EXPLAIN (costs off)
SELECT count(book_ref) FROM bookings;
2. Установите параметр enable_seqscan в off.
3. Установите параметр enable_indexonlyscan в off.
4. Восстановите значения параметров по умолчанию и установите
параметр max_parallel_workers_per_gather в 0.