Postgres/Postgres Internal

PostgreSQL Query architecture

moxie2ks 2025. 1. 31. 18:01
728x90
반응형

1. Parser

  • Query 구문을 분석 → Syntax error check
  • Parse Tree 생성
  • 이 단계에서는 System Catalog를 참조하지 않기 때문에 개별 요소들에 대한 의미분석(Semantic) 이 불가 → 단순히 문법체크(Syntax)만 수행
    📌 System Catalog : Table, Row, Schema 등의 메타데이터 정보를 저장하는 장소로 다른 RDBMS에서는 Data Dictionary라고 표기하기도 한다.

2. Semantic Parser

  • Parse Tree의 의미를 분석하여 Query Tree를 생성한다.
  • SQL이 참조하는 테이블, 함수, 연산자를 이해하기 위한 의미분석(Semantic) 과정을 수행한다.
  • Query Rewrite나 최적화가 필요하지 않은 Simple Query의 경우, 바로 Excuter 단계로 넘어간다.
    • Simple Query : CREATE, DROP, ALTER, vacuum 등의 제어문
    • Complex Query : SELECT, JOIN과 같은 그 외의 Query

3. Rewriter

  • 앞서 Semantic Parser 단계에서 생성된 Query tree에 사전 정의된 RULE을 적용하여 Query를 단순화한다.
  • 입력, 출력은 모두 Query Tree이다.
  • 뷰는 구문 트리에 하위 쿼리를 대체하여 처리된다. 아래 그림을 예를 들면, tab2가 뷰인 경우 Rewriter는 다음과 같은 것을 출력한다.

  • ON INSERT/UPDATE/DELETE 규칙은 더 많은 전체 변환이 필요하며, 단일 쿼리에서 여러 쿼리를 생성할 수 있다.
  • Query Rewrite 기법
    • Subquery Collapse : Subquery를 Main query에 병합(Subquery unset)
    • View merging : View 또는 Inline View를 풀어서 테이블 간 조인으로 변경
      • View merging이 가능한 경우 테이블 간 다양한 조인방법 및 순서를 선택할 수 있다.
      • Simple View는 항상 View Merging에 성공하지만 Complex View는 항상 실패한다.
      • 📌 Simple View와 Complex View
        View 내부에 GROUP BY, DISTINCT와 같은 Aggregation을 사용하지 않은 경우를 Simple View라고 하며 반대의 경우를 Complex View라고 한다.
    • JPPD(Join Predicate Push-Down) : View Merging이 실패할 경우 Join Predicate를 View내부로 Push-Down 하는 방법
      • Join predicate는 상수 조건이어야 Push-Down이 가능하다.
      • Lateral View를 사용해야 한다.

4. Planner / Optimizer

  • 최적의 실행계획을 생성하는 단계
  • 선택 가능한 모든 Plan Path를 만들어 각 Path의 Cost를 계산하며, 그중 가장 적은 Cost를 갖는 Plan을 선택한다.
  • 간단한 예시: SELECT * FROM t WHERE f1 < 100
    • 만약 t(f1)에 대한 인덱스가 있다면, 두 가지 실행 계획이 고려된다: • t의 모든 행을 순차적으로 스캔하는 것 • 인덱스 스캔과 함께 f1 < 100의 제한 조건
    • 각 계획의 비용(disk page fetches와 CPU 시간)이 추정되고, 추정된 비용이 낮은 계획이 선택된다.
  • 주요 결정사항(Key Decisions)으로 SCAN 및 JOIN method가 있다.
    • Path 단위 기준은 SCAN, JOIN, GROUP BY, SORTING, AGGREGATION 등이 있다.
    • Cost는 System Catalog에 저장된 통계정보를 기반으로 한다(pg_class, pg_statistics)
    • Scan method
      • Sequential Scan
        • Table을 Full scan 하면서 레코드를 읽는다.
        • 인덱스가 존재하지 않거나 선택할 수 없을 때 사용
      • Bitmap Index Scan
        • 테이블 랜덤 액세스 횟수를 줄이기 위해 고안된 방식
        • 인덱스 칼럼에 대한 테이블 레코드의 정렬 상태를 Correlation이라고 하며, Correlation 이 좋으면 Index Scan을, 나쁘면 Bitmap Index Scan 방식을 선택한다.
        • 이 방식은 Block 번호순으로 Block을 정렬한 후 Access 한다 → Index Key 순으로 출력되지 않는다(정렬이 보장되지 않는다).
        • 📌 Correlation은 Oracle의 Clustering Factor와 동일한 개념으로 인덱스와 테이블의 유사도를 의미한다.
      • Index Scan
        • Index Leaf Block에 저장된 Key를 이용하여 레코드를 읽는다.
        • 레코드의 정렬 상태에 따라 Block Access횟수가 크게 차이 난다.
        • Index Key 순으로 출력된다(정렬이 보장된다).
    • Join method
      • 우선, 각 구성 요소 테이블의 순차 스캔과 인덱스 스캔에 대한 비용을 추정한 후, 가능한 모든 쌍방향 Join 경로를 포함하는 Join tree를 구축한다.
      • k번째 레벨의 트리는 모든 기본 테이블을 Join 하는 가장 저렴한 방법을 제공한다. 최상위 수준(n-table 쿼리의 경우)에서는 전체적인 가장 저렴한 Join 경로를 찾는다.
      • 만약 많은 테이블이 관련되어 있고(10개 이상) 가능한 Join 경로의 수가 지수적으로 증가함에 따라 exhaustive search가 실현 불가능하다면, 제한된 수의 대안을 사용하여 확률적 검색을 위해 genetic optimization algorithm을 사용한다.

5. Executer

  • 실행 계획에 따라 Query를 수행하고 그 결과를 Client에 전달한다.
  • 쿼리를 처리할 때 미리 할당된 temp_buffers 및 work_mem과 같은 일부 메모리 영역을 사용하며 필요에 따라 임시 파일(Temp)을 생성할 수 있다.
  • The executor의 기본 아이디어는 계획 트리(plan tree)를 실행하는 것
  • 계획 트리는 처리 노드의 연속적인 요청-풀(demand-pull) 네트워크로 구성되어 있으며, 각 노드는 호출될 때 다음 튜플을 생성한다.
  • 상위 노드는 자식 노드를 호출하여 입력 튜플을 가져오고, 이를 기반으로 자신의 출력 튜플을 계산한다.
    • 하위 레벨 노드는 물리 테이블의 스캔이다. 일반적으로 순차 스캔 또는 인덱스 스캔이다.
    • 상위 노드는 일반적으로 조인 노드이다. nested-loop, merge, hash joins가 사용 가능하다. 각 조인 노드는 두 개의 입력 튜플 스트림을 결합하여 하나의 출력 튜플을 생성한다.
  • 특수 목적의 노드 유형이 있다. → Ex. SORT, AGGREGATE 등
  • 예시 1
  • 예시 2

참고

728x90
반응형