2P by GN⁺ | ★ favorite | 댓글과 토론
  • Postgres 19의 프로퍼티 그래프는 기존 테이블을 정점과 간선으로 선언하고 MATCH로 고정된 관계 패턴을 검색하는 SQL/PGQ 기능으로, 데이터를 복사하거나 별도 그래프 실행 엔진을 만들지 않음
  • 그래프 패턴은 관계형 조인으로 컴파일되어 기존 옵티마이저·인덱스·통계를 그대로 사용하므로, 느린 순회도 일반 조인과 같은 방식으로 분석하고 최적화할 수 있음
  • 차원 테이블은 정점, 순수 조인 테이블은 간선으로 자연스럽게 대응하지만, 여러 외래 키와 자체 속성을 가진 팩트 테이블은 이벤트를 나타내는 허브 정점으로 모델링하는 편이 적합함
  • 하나의 테이블을 서로 다른 별칭으로 정점과 여러 간선에 동시에 선언할 수 있어, results의 기존 외래 키를 이용한 별도 간선 테이블을 생성·저장할 필요가 없음
  • Postgres 19는 가변 길이 경로를 지원하지 않으므로 최단 경로·N홉 도달성·PageRank에는 맞지 않으며, 관계 구조를 미리 아는 고정 길이 탐색에 적합함

관계형 스키마를 그래프로 읽는 방식

  • 정규화된 관계형 스키마에서 drivers, constructors, circuits 같은 차원 테이블은 엔터티를 담고, 팩트 테이블은 경기 결과나 예선 세션 같은 이벤트를 기록함
  • 각 행은 잠재적인 정점이고, 외래 키는 다른 행을 가리키는 잠재적인 간선
    • ER 다이어그램은 스키마 수준의 그래프에 해당함
    • 실제 행과 외래 키 관계는 인스턴스 수준의 그래프에 해당함
  • “각 결과에 해당하는 constructor를 찾는다”는 요청은 관계형 SQL에서 results JOIN constructors ON ...로 작성하는 그래프 순회임
  • SQL/PGQ는 그래프 자체를 새로 추가하지 않고, 조인을 직접 나열하는 대신 그래프 형태로 질문을 작성하게 해줌
  • RelBench 방식은 테이블을 pandas로 가져와 메모리에서 PyTorch 그래프로 구성하지만, Postgres 프로퍼티 그래프는 데이터베이스 안의 기존 테이블 관계를 그대로 사용함
  • PyG의 예제도 주로 평면 파일이나 메모리 내 그래프를 사용함
  • Formula 1 데이터셋으로 실험한 코드는 GitHub 저장소에서 확인할 수 있음

프로퍼티 그래프의 구성

  • CREATE PROPERTY GRAPH는 기존 테이블 위에 만드는 이름 있는 선언 객체
    • VERTEX TABLES는 행을 정점으로 읽을 테이블을 지정함
    • EDGE TABLES는 행을 연결 관계로 읽을 테이블을 지정함
  • 각 정점 테이블에는 다음 요소를 정의함
    • KEY: 정점의 식별자로, 대부분 기본 키를 사용함
    • LABEL: MATCH에서 사용할 정점 유형 이름임
    • PROPERTIES: 그래프 질의에서 접근할 수 있는 열 목록임
  • 각 간선 테이블에는 SOURCEDESTINATION을 지정하며, 두 값은 각각 정점의 키를 참조함
  • CREATE PROPERTY GRAPH데이터를 이동하거나 복사하지 않음
    • 행은 원래 테이블에 그대로 남아 있음
    • 선언은 기존 외래 키 구조를 그래프로 읽는 방법만 정의함
  • 프로퍼티 그래프의 구조적 요소는 정점과 간선 두 종류뿐
    • 레이블과 프로퍼티는 정점 또는 간선에 속하는 속성임
    • psql에서 각 요소의 Element Kindvertex 또는 edge 중 하나임

MATCH로 고정된 패턴 질의하기

  • 그래프 질의는 GRAPH_TABLE(...) 안에 MATCH 패턴을 작성하는 방식임
  • 다음 패턴은 driver에서 result를 거쳐 race로 이어지는 관계를 나타냄
MATCH (d IS driver)<-[IS of_driver]-(res IS result)-[IS in_race]->(ra IS race)  
  • COLUMNS 절은 그래프 질의가 외부로 반환할 열을 지정함
  • 바깥쪽 SELECT에서는 GRAPH_TABLE(...)의 결과를 일반 테이블처럼 조회할 수 있음
  • 패턴에 방향과 레이블이 포함되어 관계를 문장처럼 읽을 수 있으므로, 동일한 다중 조인보다 구조를 파악하기 쉬움

실행 시 관계형 조인으로 변환되는 구조

  • MATCH는 Postgres에 별도로 덧붙인 그래프 실행 엔진이 아니라 관계형 조인으로 컴파일되는 문법
  • driver–result–race 패턴에 EXPLAIN을 실행하면 기반 테이블을 대상으로 한 4개의 해시 조인이 나타남
  • 직접 작성한 조인과 마찬가지로 다음 요소를 사용함
    • 기존 Postgres 옵티마이저
    • 기존 인덱스
    • 기존 통계
  • 그래프 순회가 느리다면 원인과 최적화 방법도 일반 조인이 느린 경우와 같음
  • psql에서도 테이블과 유사한 명령으로 그래프를 조사할 수 있음
    • \dG: 프로퍼티 그래프 목록을 표시함
    • \d f1: 각 정점과 간선, 기반 테이블, 요소 종류, 간선의 출발·도착 정점을 보여줌
    • \d+ f1: 전체 CREATE PROPERTY GRAPH 문을 재구성하며, 간선 테이블의 기본 키에서 추론한 간선 키도 포함함

키와 프로퍼티의 차이

  • 정점의 KEY로 지정한 열이 자동으로 프로퍼티가 되지는 않음
  • driver_id를 정점 키로만 지정한 뒤 d.driver_id = 1로 필터링하면, 해당 열이 질의 가능한 프로퍼티가 아니므로 실패함
  • 키는 정점을 식별하지만 질의에 노출하지는 않음
  • ID 열을 필터링하거나 반환하려면 PROPERTIES 목록에 명시적으로 추가해야 함

Postgres 19의 가변 길이 경로 제약

  • Postgres 19는 간선을 1~3회 따라가는 식의 요소 패턴 수량자를 지원하지 않음
ERROR: element pattern quantifier is not supported  
  • 두 홉을 탐색하려면 MATCH에 간선 패턴 두 개를 명시해야 함
  • 임의 길이의 경로는 프로퍼티 그래프 문법으로 표현할 수 없음
  • 깊이가 열려 있는 탐색은 기반 테이블에 대한 재귀 CTE로 처리해야 하며, 이 경우 프로퍼티 그래프 문법을 벗어남

기존 테이블을 정점과 간선으로 매핑하기

  • 차원 테이블은 정점

    • drivers, constructors, circuits처럼 안정적인 기본 키와 속성을 가진 엔터티 테이블은 정점으로 직접 대응함
    • 기본 키를 정점 키로 사용하고 필요한 열을 프로퍼티로 노출하면 됨
  • 순수 조인 테이블은 간선

    • student_courses(student_id, course_id) 같은 다대다 브리지 테이블은 두 엔터티를 연결하는 것이 본래 역할이므로 간선으로 자연스럽게 대응
    • 한쪽 외래 키를 출발 정점, 다른 쪽 외래 키를 도착 정점으로 선언함
    • 조인 테이블의 각 행 자체가 하나의 관계이므로 별도의 그래프용 데이터 변환이 필요하지 않음
    • 이 구조에서는 studentscourses가 정점이고, student_coursesenrolled_in 간선이 됨
  • 팩트 테이블은 이벤트 정점

    • results 행은 driver, race, constructor라는 세 엔터티를 가리키면서 grid, position, points, status 같은 자체 데이터도 가짐
    • SQL/PGQ 간선은 출발점 하나와 도착점 하나를 가진 이항 관계이므로, 세 개의 외래 키를 가진 행 전체를 단일 간선으로 만들 수 없음
    • 팩트 행 자체가 분석 대상이라면 해당 테이블을 정점으로 선언하는 편이 적합함
    • results 정점은 이벤트와 그 속성을 보유함
    • results_driver, results_race, results_constructor 같은 좁은 간선이 외부 엔터티와 연결함
    • 이 모델은 driver <- result -> race 같은 허브 구조를 만들며, result 정점에서 멈춰 필터링하거나 속성을 조회할 수 있음
    • 관계 자체가 관심 대상이면 간선으로, 고유 속성을 가진 특정 행이나 이벤트가 관심 대상이면 정점으로 모델링함
    • 조인 테이블은 관계를 나타내고, 팩트 테이블은 이벤트를 나타냄

하나의 테이블을 정점과 간선으로 동시에 사용하기

  • “정점 또는 간선”이라는 구분은 기반 테이블이 아니라 그래프의 각 요소 선언에 적용됨
  • 하나의 테이블을 서로 다른 별칭으로 VERTEX TABLESEDGE TABLES 양쪽에 선언할 수 있음
  • results를 result 정점으로 사용하면서 같은 테이블을 다음 간선 별칭으로 재사용할 수 있음
    • results AS res_driver: result에서 driver로 연결함
    • results AS res_race: result에서 race로 연결함
    • results AS res_constr: result에서 constructor로 연결함
  • 각 간선 별칭은 results에 이미 존재하는 기본 키와 외래 키 열을 사용함
  • 별도의 results_driver, results_race, results_constructor 테이블을 만들 필요가 없으며, 별칭은 데이터를 저장하지 않음
  • 외래 키가 세 개인 팩트 테이블은 하나의 다중 종점 간선이 아니라, 각각 출발점과 도착점이 하나인 간선 별칭 세 개로 선언해야 함
  • 이 방식은 세 개의 물리 테이블이나 뷰 대신 하나의 기반 테이블에 대한 세 개의 선언적 별칭을 사용함

프로퍼티 이름과 타입 충돌

  • PROPERTIES를 생략하면 SQL/PGQ는 테이블의 모든 열을 프로퍼티로 노출
  • resultsqualifying에 모두 number 열이 있지만 각각 double precisionbigint라면 다음 오류가 발생함
ERROR: property "number" data type mismatch: double precision vs. bigint  
  • 그래프 전체에서 같은 이름의 프로퍼티는 하나의 타입을 가져야 하므로, 서로 다른 타입의 동명 열이 충돌함
  • 필요한 열만 PROPERTIES에 명시하면 충돌하는 열을 그래프에서 제외할 수 있음
  • 키가 자동으로 프로퍼티가 되지 않는 문제와 타입 충돌은 모두 명시적인 프로퍼티 허용 목록으로 해결 가능함

적합한 질의와 부적합한 질의

  • 고정된 관계 패턴

    • 프로퍼티 그래프는 “X와 이 특정 패턴으로 연결된 대상을 찾아라”라는 질문에 적합함
    • Formula 1 그래프에서는 다음과 같은 질의를 작성할 수 있음
      1. 특정 driver가 어느 constructor에서 경주했는지 탐색함
      2. driver에서 result와 race를 거쳐 다른 result와 driver로 이어지는 패턴으로 경쟁자를 찾음
      3. 출발 순위가 10위 밖이고 driver가 Italian인 결과처럼 구조와 속성 조건을 함께 필터링함
    • 관계의 모양을 미리 알고 그 구조가 고정되고 유한할 때 검색·필터링·집계 문법의 가독성이 높아짐
    • 여러 번의 자기 조인이 필요한 질의도 하나의 MATCH 패턴으로 읽기 쉽게 표현할 수 있음
  • 경로 자체가 미지수인 문제

    • 다음 문제는 Postgres 19 프로퍼티 그래프에 맞지 않음
      • 두 driver 사이의 최단 경로
      • N홉 안에서 도달할 수 있는 모든 대상
      • 깊이를 미리 알 수 없는 연결 탐색
    • 이런 질의는 가변 길이 순회가 필요하므로 기반 테이블에 대한 재귀 CTE를 사용해야 함
    • PageRank, 커뮤니티 탐지, 중심성 계산 같은 그래프 알고리듬도 패턴 매칭과 다른 문제이므로 이 기능의 범위에 속하지 않음
    • 프로퍼티 그래프는 경로 구조를 알고 있을 때 연결 대상을 찾지만, X와 Y가 어떤 경로로 연결됐는지 모르는 상태에서 경로를 발견하거나 그래프의 구조적 중요도를 계산하지는 못함

도입 전 판단할 점

  • SQL/PGQ는 기존 외래 키 구조 위에 놓이는 선언적 오버레이이며, 별도로 선택하지 않는 한 추가 데이터를 저장하지 않음
  • 주요 이점은 두 가지임
    • 고정된 형태의 순회를 관계형 조인보다 읽기 쉽게 작성할 수 있음
    • 스키마를 그래프로 문서화하는 이름 있는 객체를 제공함
  • 가장 큰 제약은 가변 길이 경로가 없어 깊거나 개방된 탐색을 재귀 SQL로 처리해야 한다는 점임
  • 테이블 자체가 본질적으로 정점이나 간선인 것은 아니며, 각 그래프에서 그 행을 정점·간선 또는 양쪽으로 읽을지 결정할 수 있음
  • Postgres 19에서 기존 스키마에 고정된 그래프 패턴을 질의하려는 경우, 별도 그래프 데이터베이스로 데이터를 옮기기 전에 CREATE PROPERTY GRAPHMATCH를 검토할 수 있음
  • 알 수 없는 깊이를 탐색하려면 재귀 CTE가 필요하지만, 어느 방식을 사용하든 데이터가 Postgres를 떠날 필요는 없음
  • 실제 도입 전에는 필요한 성능이 나오는지 직접 테스트해야 함

댓글과 토론