병렬 쿼리를 언제 쓸 수 있나

병렬 쿼리를 언제 쓸 수 있나 (When Can Parallel Query Be Used?)

병렬 쿼리가 좋다는 건 알겠는데, "내 모든 쿼리가 병렬로 실행되나요?"라고 묻는다면 대답은 아니에요. 몇 가지 설정과 조건이 있어야 병렬 쿼리 계획이 만들어질 수 있어요. 어떤 경우엔 쿼리 전혀 병렬로 실행되지 않고, 어떤 경우엔 계획은 세워졌는데 실행 시점에 병렬이 불가능해지기도 해요. 세 가지 경우를 나눠서 볼게요.

출처: 공식문서

병렬 쿼리 계획이 아예 생성되기 위한 설정

여러 설정이 쿼리 플래너가 어떤 상황에서도 병렬 쿼리 계획을 만들지 못하게 할 수 있어요. 병렬 쿼리 계획이 조금이라도 생성되려면 다음 설정이 그렇게 구성되어야 해요.

  • max_parallel_workers_per_gather0보다 큰 값으로 설정되어야 해요. 이것은 "구성된 max_parallel_workers_per_gather보다 많은 워커를 쓰지 말 것"이라는 더 일반적인 원칙의 특수한 경우예요.
  • 시스템이 **단일 사용자 모드(single-user mode)**로 실행 중이면 안 돼요. 이 상황에서는 전체 데이터베이스 시스템이 단일 프로세스로 실행되므로 백그라운드 워커가 없거든요.

계획 생성이 막히는 쿼리 조건

일반적으로 병렬 계획이 가능하더라도, 다음 중 하나가 참이면 플래너는 주어진 쿼리에 대해 병렬 계획을 만들지 않아요.

데이터를 쓰거나 행을 잠그는 쿼리. 쿼리가 최상위에서든 CTE 안에서든 데이터 수정 연산을 포함하면 그 쿼리의 병렬 계획은 만들어지지 않아요. 예외로, 새 테이블을 만들고 채우는 다음 명령들은 쿼리의 밑바탕 SELECT 부분에 병렬 계획을 쓸 수 있어요:

  • CREATE TABLE ... AS
  • SELECT INTO
  • CREATE MATERIALIZED VIEW
  • REFRESH MATERIALIZED VIEW

실행 중 일시 중지될 수 있는 쿼리. 시스템이 부분·점진적 실행이 일어날 수 있다고 생각하는 상황에서는 병렬 계획을 만들지 않아요. 예를 들어 DECLARE CURSOR로 만든 커서는 병렬 계획을 절대 쓰지 않아요. 마찬가지로 FOR x IN query LOOP .. END LOOP 형태의 PL/pgSQL 루프도 병렬 계획을 절대 쓰지 않아요. 병렬 쿼리 시스템이 루프 안의 코드가 병렬 쿼리 활성 중에 안전하게 실행될 수 있는지 검증할 수 없기 때문이에요.

PARALLEL UNSAFE로 표시된 함수를 쓰는 쿼리. 대부분의 시스템 정의 함수는 PARALLEL SAFE지만, 사용자 정의 함수는 기본적으로 PARALLEL UNSAFE로 표시돼요. (15.4절 참고.)

이미 병렬인 다른 쿼리 안에서 실행되는 쿼리. 예를 들어 병렬 쿼리가 호출한 함수가 스스로 SQL 쿼리를 발행하면, 그 쿼리는 병렬 계획을 절대 쓰지 않아요. 이것은 현재 구현의 한계이지만, 이 한계를 없애는 게 바람직하지 않을 수도 있어요. 한 쿼리가 매우 많은 프로세스를 쓸 수 있게 되기 때문이죠.

계획은 있는데 실행이 병렬로 불가능한 경우

특정 쿼리에 병렬 쿼리 계획이 생성되더라도, 실행 시점에 그 계획을 병렬로 실행하는 게 불가능해지는 여러 상황이 있어요. 그런 경우 리더가 Gather 노드 아래의 계획 부분을 마치 Gather 노드가 없는 것처럼 전적으로 혼자서 실행해요. 다음 조건 중 하나라도 맞으면 그렇게 돼요.

  • 백그라운드 워커 총 수 제한 때문에 워커를 얻지 못하는 경우. 총 백그라운드 워커 수는 max_worker_processes를 초과할 수 없어요.
  • 병렬 쿼리용 워커 수 제한 때문에 워커를 얻지 못하는 경우. 병렬 쿼리 목적으로 시작된 백그라운드 워커의 총 수는 max_parallel_workers를 초과할 수 없어요.
  • 클라이언트가 0이 아닌 fetch count로 Execute 메시지를 보내는 경우. 확장 쿼리 프로토콜(extended query protocol)의 설명을 참고하세요. libpq는 현재 그런 메시지를 보낼 방법을 제공하지 않으므로, libpq에 의존하지 않는 클라이언트를 쓸 때만 발생할 수 있어요. 이런 일이 잦다면, 가능성 있는 세션에서는 max_parallel_workers_per_gather를 0으로 설정해서 직렬 실행 시 아최적일 수 있는 쿼리 계획을 애초에 생성하지 않는 게 좋아요.

더 알아보기 (Learn more)