본문 바로가기

JanusGraph/JanusGraph 실습

JanusGraph 실습 1 / 스키마와 데이터 모델링

✅ 스키마 구성 3요소

  1. 정점 라벨 (Vertex Label)
  2. 간선 라벨 (Edge Label)
  3. 속성 키 (Property Key)

이들을 정의해야만 정점, 간선, 속성을 삽입할 수 있습니다.

 


🧩 1. 간선 라벨 정의 (Edge Label)

knows = management.makeEdgeLabel('knows').multiplicity(MULTI).make();
  • management: 스키마를 다루는 객체
  • makeEdgeLabel('knows'): knows라는 간선 라벨 생성
  • multiplicity(MULTI): 다중 연결 허용
  • make(): 라벨 생성 완료

📌 Multiplicity(다중도)의 의미

두 정점 사이에서 특정 간선 라벨이 몇 개까지 허용되는지를 설정하는 제약 조건

🎯 Multiplicity 종류


유형 설명
MULTI 정점 간 동일 라벨 간선여러 개 허용 (제약 없음)
SIMPLE 정점 간 동일 라벨 간선을 1개만 허용
MANY2ONE 한 정점에서 1개만 외부로 나가는 간선 허용
ONE2MANY 한 정점으로 들어오는 간선 1개만 허용
ONE2ONE 양방향으로 단 하나의 간선만 허용

💡 예시:

  • MANY2ONE: 사람 → 어머니 관계
  • ONE2MANY: 유저 ← 이메일 주소
  • SIMPLE: 유저 간 친구 관계
  • MULTI: 사람 ↔ 사람 (추천, 팔로우 등 중복 허용)

🧩 2. 정점 라벨 정의 (Vertex Label)

person = management.makeVertexLabel('person').make();
  • makeVertexLabel('person'): person이라는 정점 라벨을 생성
  • 이후 이 라벨을 정점 생성 시 지정 가능

🧩 3. 속성 키 정의 (Property Key)

name = management.makePropertyKey('name').dataType(String.class).make();
  • makePropertyKey('name'): 속성 이름 설정
  • dataType(String.class): 자료형 지정 (문자열)
  • make(): 속성 키 생성 완료

📌 JanusGraph 지원 자료형 목록

String 문자열
Character 문자형
Boolean 불리언 (true/false)
Byte 바이트
Short 짧은 정수
Integer 정수
Long 긴 정수
Float 4바이트 실수
Double 8바이트 실수
Date 시간 정보 (java.util.Date)
Geoshape 점, 원, 박스 등의 위치 정보 표현
UUID 전역 고유 식별자

📍 Geoshape는 위도/경도를 이용해 공간 데이터를 표현할 수 있으며,
📍 UUID는 전 세계적으로 유일한 식별자입니다.


🧩 4. Cardinality (카디널리티)

속성(Property)에 값이 몇 개까지 저장될 수 있는지를 제한하는 제약 조건

🎯 Cardinality 종류

Cardinality 종류

SINGLE 단일 값만 저장 (기본값)
LIST 여러 값 저장 가능, 중복 허용
SET 여러 값 저장 가능, 중복 불허
name = management.makePropertyKey("name").dataType(String.class).cardinality(Cardinality.LIST).make();

 

✅ 전체 흐름 설명

정점 라벨 생성 makeVertexLabel("person").make()
간선 라벨 생성 makeEdgeLabel("knows").multiplicity(MULTI).make()
속성 키 생성 makePropertyKey("name").dataType(String.class).make()
카디널리티 설정 .cardinality(Cardinality.SET)

하지만, 이렇게 일일이 스키마를 지정해줄 필요가 없다!, 최적화를 할 순 있겠지만...

🔧 Automated Schema Maker!

자동 스키마 생성기(Automatic Schema Maker)는 JanusGraph에서 정점(Vertex), 간선(Edge), 속성(Property)을 삽입할 때

스키마가 사전 정의되어 있지 않으면 자동으로 생성해주는 기능입니다.

 

🧩 작동 방식 요약

  • 예를 들어 다음과 같은 쿼리를 수행한다고 가정해봅시다:
g.addV('person').property('name', 'Jinkwon')
  • 이때 'person'이라는 정점 라벨, 'name'이라는 속성 키가 사전에 정의되지 않았어도, JanusGraph가 내부적으로 다음과 같은 자동 스키마 생성을 수행합니다:
요소 자동 생성 내용
Vertex Label 'person'이라는 정점 라벨 생성
Property Key 'name'이라는 속성 키 생성 (기본 자료형: String, 기본 카디널리티: SINGLE)

⚙️ 자동 스키마 생성의 설정 위치

  • 이 기능은 JanusGraph 설정 파일이나 자바 코드 내 설정을 통해 제어할 수 있습니다.
 
schema.default=none

none 스키마는 자동 생성되지 않음. 명시적으로 스키마를 정의해야 함
default 속성 키는 String, 카디널리티는 SINGLE로 자동 생성됨
ignore 스키마 없이 삽입 시 예외 발생 ❗(스키마 강제 모드)

🔍 예제 기반 설명

📌 기존 스키마

mgmt = graph.openManagement()

email = mgmt.makePropertyKey("email").dataType(String.class).cardinality(Cardinality.SINGLE).make()
mgmt.commit()
 

🧨 문제 발생

  • 수십만 개 유저 중 이메일이 2개 이상인 사용자가 필요해짐.
  • 하지만 Cardinality.SINGLE로 되어 있어 이메일 하나만 저장 가능.
  • 또, 인덱스가 없어 검색 속도도 느림.

🧩 1. Cardinality 변경 → 중복 허용으로 확장

🔄 변경 전:

email = mgmt.makePropertyKey("email").dataType(String.class).cardinality(Cardinality.SINGLE).make()
 

🔄 변경 후 (LIST로 변경):

불가! 이미 커밋된 스키마는 직접 수정할 수 없습니다.
대신, 새로운 속성 키를 생성해서 대체해야 합니다.

// 새 속성키 정의
email_multi = mgmt.makePropertyKey("email_multi").dataType(String.class).cardinality(Cardinality.LIST).make()
mgmt.commit()

// 기존 데이터 마이그레이션 (코드로 수행 필요)
g.V().hasLabel('user').property('email_multi', g.V().values('email').next())

📈 효과:

변경 전 변경 후
이메일 1개만 저장 가능 이메일 다수 저장 가능 (중복 포함)
속성 이름 email 새로운 속성 email_multi
검색 시 조건 제한 유연한 저장 구조

🧩 2. 인덱스 추가 → 쿼리 성능 개선

기존: 인덱스 없음

// 쿼리
g.V().hasLabel("user").has("email", "a@b.com")
→ 전수 스캔 (느림)

변경: 인덱스 추가

mgmt = graph.openManagement()
mgmt.buildIndex("userByEmail", Vertex.class)
    .addKey(email)
    .unique()
    .indexOnly(mgmt.getVertexLabel("user"))
    .buildCompositeIndex()
mgmt.commit()​

 

라인별 설명

🔹 mgmt = graph.openManagement()

  • 의미: 스키마를 설정하거나 변경하기 위해 JanusGraph의 스키마 관리 세션을 시작합니다.
  • 이 상태에서는 속성 키, 라벨, 인덱스를 정의하거나 조회할 수 있습니다.

🔹 mgmt.buildIndex("userByEmail", Vertex.class)

  • 의미: userByEmail이라는 이름의 정점 인덱스를 새로 만듭니다.
  • Vertex.class: 이 인덱스는 정점(Vertex)에 대한 인덱스임을 나타냅니다.

🔹 .addKey(email)

  • 의미: 인덱스가 적용될 속성 키를 지정합니다.
  • 여기서는 email 속성에 대해 인덱스를 생성합니다.
  • 이 키는 앞서 정의한 email = mgmt.makePropertyKey(...)로 선언된 속성입니다.

🔹 .unique()

  • 의미: 이 인덱스에서 email 값은 유일해야 함을 명시합니다.
  • 즉, 동일한 email을 가진 정점은 2개 이상 존재할 수 없습니다.
  • 데이터 무결성을 보장할 때 매우 중요합니다.

🔹 .indexOnly(mgmt.getVertexLabel("user"))

  • 의미: 이 인덱스는 user라는 정점 라벨을 가진 정점에만 적용됩니다.
  • 예: company 정점에 있는 email 속성은 이 인덱스를 사용하지 않음 → 인덱싱 범위를 제한하여 성능과 저장 공간 최적화.

🔹 .buildCompositeIndex()

  • 의미: Composite Index를 생성합니다.
    • 정적인 값 매칭(예: email = "a@b.com")에 최적화됨
    • 부분일치, 정렬, 범위검색은 불가능
  • 장점: 빠르고 간결하며 기본 검색 쿼리에 매우 적합

🔹 mgmt.commit()

  • 의미: 위에서 정의한 스키마 변경 사항을 JanusGraph에 영구 반영합니다.
  • 스키마 변경은 commit() 전까지는 적용되지 않음

주의: 인덱스 생성 후에는 반드시 reindexing이 필요 
// 인덱스 생성 후 활성화까지 기다리기 (관리자 콘솔에서 확인)
graph.tx().rollback()
ManagementSystem.awaitGraphIndexStatus(graph, "userByEmail").call()

// 인덱싱 시작
mgmt = graph.openManagement()
mgmt.updateIndex(mgmt.getGraphIndex("userByEmail"), SchemaAction.REINDEX).get()
mgmt.commit()

📈 효과:

항목 변경 전 변경 후
쿼리 방식 속성 기반 순차 탐색 인덱스 기반 빠른 조회
성능 느림 (O(N)) 빠름 (O(logN) 혹은 O(1))
조건 필터링 불완전 정확한 조건 검색 가능

🧩 3. Multiplicity 수정 → 관계 제약 강화

예시: 한 유저가 하나의 어머니만 갖도록 제약

변경 전:

mgmt.makeEdgeLabel("has_mother").multiplicity(MULTI).make()

변경 후:

mgmt.makeEdgeLabel("has_mother").multiplicity(MANY2ONE).make()

📈 효과:

항목 변경 전 (MULTI) 변경 후 (MANY2ONE)
제약 없음 유저가 어머니를 여러 명 가질 수 있음 하나의 정점에서 단 하나의 간선만 허용 (어머니 하나만)
무결성 위험 잘못된 모델링 가능 정확한 의미 표현 + 데이터 무결성 유지
예외 처리 없음 중복 연결 시 오류 발생 → 버그 조기 탐지

🎯 중간에 스키마를 조정하면?

변경 항목 목적 기대 효과
Cardinality 변경 속성 중복 허용 / 제한 유연한 데이터 표현
Edge Multiplicity 조정 관계 모델링 정교화 데이터 무결성 강화
Index 추가 검색 성능 향상 실시간 쿼리 가능
indexOnly(vertexLabel) 불필요한 인덱스 방지 저장 공간 최적화

📌 결론: 스키마를 미리 정의하는 이유

  • 💾 데이터 무결성 유지
  • 🚀 쿼리 성능 향상
  • 🧠 명확한 구조 정의를 통해 협업과 유지보수 용이

++++ JanusGraph 인덱스 요약 정리

JanusGraph는 그래프 데이터베이스의 쿼리 성능을 높이기 위해 인덱스 시스템을 사용합니다.
2가지 종류의 인덱스가 있으며, 각각 목적과 용도가 다릅니다.


✅ 1. 그래프 인덱스 (Graph Index)

정점 또는 간선을 속성 기반으로 빠르게 찾기 위한 인덱스
g.V().has("key", "value") 형태의 쿼리를 빠르게 처리하기 위해 사용

🔹 용도

  • 단순 필터 작업 (has 조건 검색)
  • 전체 목록에서 조건에 맞는 정점/간선 빠르게 조회

🔹 종류

복합 인덱스 (Composite Index) JanusGraph 내부에서만 사용됨
정확한 값 일치 검색에 최적화
예: email = 'abc@xyz.com'
혼합 인덱스 (Mixed Index) 외부 인덱싱 엔진과 연동 (예: Elasticsearch, Solr)
부분 검색, 정렬, 범위 검색 가능
예: name contains 'kwon', age > 30

✅ 2. 정점 중심 인덱스 (Vertex-Centric Index)

특정 정점에 연결된 간선 중에서 조건에 따라 빠르게 필터링할 수 있도록 하는 인덱스

🔹 용도

  • 순회(Traversal) 성능 향상
  • 예: 하나의 정점에서 수천 개의 연결된 간선 중 일부만 조회하고 싶은 경우
  • E() 또는 outE("follows")와 같은 연산 시 최적화

🔹 예시 상황

  • 한 유저가 수천 명을 팔로우하고 있음
  • "최근 팔로우한 10명"만 보고 싶을 때
  • 정점 중심 인덱스를 사용하면 이 간선들 중 원하는 조건의 일부만 빠르게 가져올 수 있음

✅ 핵심 비교 요약

항목 그래프 인덱스 정점 중심 인덱스
대상 전체 그래프 (정점/간선) 특정 정점에서의 간선
용도 속성 기반 조회 정점 연결 순회 최적화
예시 g.V().has("email", "abc@x.com") g.V().outE("follows").has("since", gt("2023-01-01"))
종류 복합, 혼합 단일 타입 (간선 인덱스)
성능 조건 필터에 탁월 연결 간선 필터에 탁월

✅ 정리 문장

그래프 인덱스는 has() 쿼리 등에서 조건 필터를 빠르게 적용하기 위해 사용되며,
정점 중심 인덱스는 많은 간선 중 조건에 맞는 연결을 빠르게 탐색하기 위한 기능입니다.