본문 바로가기

PostgreSQL

PostgreSQL 병렬 처리

병렬 처리는 사용되는 대부분의 경우 수행시간을 단축시킨다.

다만 현재 PostgreSQL 최신 버전 기준으로는 병렬처리가 되는 경우가 몇몇 구문에 제약되고 그 제약된 중에서도 병렬 처리가 되지 않는 케이스들이 있다.

이에 대해 정리한다.

현재 16버전 기준으로 Parallel이 동작하는 구들은 다음과 같다(index 생성, vacuum 등의 관리 명령어들 제외)

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

관련하여서도 파라미터들이 있는데 그 값들을 잘못 설정하는 경우 병렬 쿼리 플랜이 생성되지 않는다. 아래의 설정들은 병렬 처리가 수행되기 위해 필요하다

  • max_parallel_workers_per_gather가 0보다 큰 값으로 설정되어 있어야 한다.
  • 추가로 해당 시스템이 싱글유저 모드로 동작하지 않고 있어야 한다
    • 싱글유저 모드에서는 싱글 프로세스로만 동작하기 때문.;백그라운드 워커들이 존재하지 않는다.

패러럴 쿼리가 동작하는 것이 일반적으로 가능한 경우라도 아래의 경우에 해당된다면 플래너가 패러럴 쿼리를 생성하지 않는다.

  • row에 lock을 잡거나 데이터를 쓰는 쿼리. 만약 어떤 쿼리가 데이터-수정 작업을 최상위 레벨이나 CTE 내에서 하는 경우 패러럴 플랜은 생성되지 않는다. 이에 대한 예외사항으로 CTAS 명령이나 MVIEW 생성 구문은 패러럴 플랜을 사용한다.
  • 쿼리는 실행 중 중지될 수 있다. 만약 시스템이 일부 또는 증가되는 실행이 일어날 수 있다고 보면 패러럴 플랜은 생성되지 않는다. 예를 들어 DECLARE CURSOR를 사용하여 생성된 커서는 절대 패러럴 플랜을 사용하지 않는다. PL/pgSQL에서 FOR.. x.. IN query LOOP .. END LOOP 또한 패러럴 플랜을 사용하지 않는다. 그 이유는 패러럴 쿼리 시스템이 해당 반복문이 패러럴 쿼리에 사용되어도 안전한지를 확인할 수 없기 때문이다.
  • parallel unsafe로 정의된 function을 사용하는 쿼리인 경우. 거의 모든 시스템-정의 함수들은 Parallel Safe 이지만 유저가 생성한 함수들은 기본값이 Parallel Unsafe이다.Section 15.4.
  • 패러럴로 이미 동작하는 쿼리의 일부인 경우. 예를 들면 함수가 패러럴 쿼리에서 호출되는 경우, 해당 쿼리는 패러럴 플랜을 절대로 사용하지 않는다. 이는 현재 구현된 부분에서의 제약사항이지만, 쿼리 하나가 매우 많은 프로세스들을 사용할 수 있어 이 제약을 없애는 것은 바람직하지 않을 수 있다.

심지어 패러럴 플랜이 특정 쿼리에 대해 생성되었다고 해도 실행시에 병렬로 수행되는 것이 불가능한 몇 가지 상황들이 있다. 그런 상황이 발생하면, 리더(프로세스)는 Gather 노드 아래의 플랜 부분을 혼자 실행하며 이는 Gather 노드가 존재하지 않는 것과 마찬가지이다. 아래의 상황들에서 그런 동작방식을 가진다.

  • max_worker_processes 값으로 정해놓은 백그라운드 워커의 개수가 제한되어 있는 경우
  • max_parallel_workers 값으로 패러럴 쿼리에 대한 워커 개수가 제한되어 있는 경우
  • 클라이언트가 0이 아닌 fetch count인 실행 메시지를 보낼 때. 더 자세한 내용은 extended query protocol을 참고하라. 현재 libpq가 그런 종류의 메시지를 보내는 것이 가능하지 않기 때문에 해당 메시지는 libpq를 사용하지 않는 클라이언트로부터 발생할 수 있다. 이런 일이 자주 발생한다면 자주 발생하는 세션에 max_parallel_worker_per_gather를 0으로 설정하는 것이 좋은 아이디어일 수 있다. 이유인즉 쿼리 플랜을 생성하는 것을 방지하는 것이 순차적으로(serially) 실행할 때 효과적일 수 있기 때문이다.
  • 관련 파라미터
set parallel_setup_cost = 100; 
-- 패러럴 시작 코스트 변경 set force_parallel_mode = on; -- 패러럴 강제(하지만 더 적은 코스트가 아니면 강제되지 않음) 

set parallel_tuple_cost = 0.0001; 
-- 패러럴 튜플 코스트 변경(조건절 없을때 패러럴 처리할 경우 작게 해야 함) 

set max_parallel_workers_per_gather = 8; 
-- 다른 worker 개수 제약 없는 상태에서 해당 파라미터로 제어
  • 힌트 사용 예시
select /*+ Parallel(c1 8 hard) 
set(parallel_tuple_cost 0.001) */ ....;  
select /*+ set(max_parallel_workers_per_gather 8) set(parallel_tuple_cost 0.001) */ ....;
  • 함수
만약 유저가 생성한 함수를 사용하는 경우에는 해당 함수 DDL에 PARALLEL SAFE 내용 추가해주어야 
하지만 실제로 parallel safe한지 확인해야 함.