✅ 스키마 구성 3요소
- 정점 라벨 (Vertex Label)
- 간선 라벨 (Edge Label)
- 속성 키 (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 종류
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() 쿼리 등에서 조건 필터를 빠르게 적용하기 위해 사용되며,
정점 중심 인덱스는 많은 간선 중 조건에 맞는 연결을 빠르게 탐색하기 위한 기능입니다.
'JanusGraph > JanusGraph 실습' 카테고리의 다른 글
[실습] Gremlin vs Cypher 상황별 성능 비교 실험 설계 (0) | 2025.04.21 |
---|---|
JanusGraph 실습 4 / import & export (0) | 2025.04.18 |
JanusGraph 실습 3 / Hbase 설치 및 활용 가이드 (0) | 2025.04.18 |
JanusGraph 실습 2 / Cassandra 설치 및 활용 가이드 (0) | 2025.04.17 |