15.4. Параллельная безопасность#

15.4. Параллельная безопасность

15.4. Параллельная безопасность #

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

Следующие операции всегда ограничены параллельно:

  • Сканирование общих табличных выражений (CTE).

  • Сканирование временных таблиц.

  • Сканирование внешних таблиц, если у обертки внешних данных есть API IsForeignScanParallelSafe, указывающий обратное.

  • Узлы плана, к которым прикреплен InitPlan.

  • Узлы плана, которые ссылается на коррелированный SubPlan.

15.4.1. Параллельная маркировка для функций и агрегатов #

Планировщик не может автоматически определить, является ли пользовательская функция или агрегат безопасным для параллельного выполнения, ограниченным параллельным выполнением или небезопасным для параллельного выполнения, поскольку это потребовало бы предсказания каждой операции, которую функция могла бы выполнить. Обычно это эквивалентно проблеме остановки и, следовательно, невозможно. Даже для простых функций, где это, возможно, могло бы быть сделано, мы не пытаемся это делать, поскольку это было бы затратно и подвержено ошибкам. Вместо этого предполагается, что все пользовательские функции небезопасны для параллельного выполнения, если они не помечены иначе. При использовании CREATE FUNCTION или ALTER FUNCTION пометки могут быть установлены, указав PARALLEL SAFE, PARALLEL RESTRICTED или PARALLEL UNSAFE по мере необходимости. При использовании CREATE AGGREGATE опцию PARALLEL можно указать со значением SAFE, RESTRICTED или UNSAFE.

Все функции и агрегаты должны быть помечены как PARALLEL UNSAFE, если они записывают в базу данных, обращаются к последовательностям, изменяют состояние транзакции, даже временно (например, функция PL/pgSQL, которая устанавливает блок EXCEPTION для перехвата ошибок) или вносят постоянные изменения в настройки. Аналогично, функции должны быть помечены как PARALLEL RESTRICTED, если они обращаются к временным таблицам, состоянию клиентского подключения, курсорам, подготовленным операторам или различным локальным состояниям бэкенда, которые система не может синхронизировать между рабочими процессами. Например, функции setseed и random являются ограниченными для параллельного выполнения по этой последней причине.

Обычно, если функция помечена как безопасная, когда она ограничена или небезопасна, или если она помечена как ограниченная, когда она на самом деле небезопасна, она может вызывать ошибки или давать неправильные ответы при использовании в параллельном запросе. Функции на языке C в теории могут проявлять полностью неопределенное поведение, если они помечены неправильно, так как система не может защитить себя от произвольного кода на C, но в большинстве случаев результат будет не хуже, чем для любой другой функции. Если есть сомнения, вероятно, лучше пометить функции как UNSAFE.

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

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