4 BUILDING KNOWLEDGE GRAPHS · CHAPTER 4

지식 그래프 데이터 적재

Loading Knowledge Graph Data — Three Paths for Bulk Import

갱신에는 더 깊이 다룰 가치가 있는 특수한 경우가 있다 — 지식 그래프의 전체 수명주기에 걸쳐 유용한 대량 임포트(bulk import)다. 이 장은 세 가지 대량 적재 방법을 다룬다. 시각적 도구로 구조를 정의하고 데이터를 매핑하는 법, 라이브 지식 그래프에 대량 데이터를 점진적으로 적재하는 법, 그리고 방대한 데이터로 지식 그래프를 부트스트랩하는 법이다.

Jesús Barrasa · Jim Webber O'Reilly Media, 2023 Part I. Graph Fundamentals pp. 51–64
Three Paths

대량 적재의 세 가지 경로

이 장의 세 기법은 각각 다른 지점에서 빛난다. Neo4j Data Importer는 도메인 모델을 그래프로 그리고 그 위에 CSV 데이터를 얹는 시각적 도구이고, LOAD CSV는 라이브 데이터베이스에 Cypher로 스크립팅하는 온라인 적재이며, neo4j-admin import는 초기 부트스트랩을 위한 초고속 오프라인 임포터다.

CSV FILES people.csv friends.csv places.csv … ① Neo4j Data Importer 시각적 모델링 + CSV 매핑 · 입문에 최적 ONLINE · Cypher 자동 생성 ② LOAD CSV Cypher 스크립팅 · 수명주기 내내 사용 ONLINE · 웹/파일시스템 · 배치 트랜잭션 ③ neo4j-admin import 초기 부트스트랩 · 수십억 레코드 OFFLINE · ~1,000,000 records/sec 지식 그래프
세 가지 적재 경로 · ①·②는 데이터베이스가 서비스 중인 상태로(온라인), ③은 정지 상태에서(오프라인) 실행된다
Note — 작게 시작하라

이 장의 어느 기법이든, 작게 시작하는 것이 합리적이다. 데이터의 작고 대표성 있는 조각을 떼어 데이터베이스에 임포트하고, 전체 임포트 작업을 돌리기 전에 모델이 의도대로 동작하는지 확인하라. 빠른 점검 하나가 수 분에서 수 시간의 좌절을 아껴 준다.

Neo4j Data Importer

Data Importer — 그리고, 얹고, 흘려보낸다

세 가지 인기 도구 중 가장 쉬운 것부터 시작한다. Neo4j Data Importer는 도메인 모델을 그래프로 그린 뒤, 노드와 관계의 데이터를 담은 CSV 파일을 그 그래프 위에 겹쳐 얹는(overlay) 시각적 도구다. 특히 입문자의 지식 그래프 부트스트래핑을 크게 단순화한다 — 다만 데이터가 CSV 파일로 준비되어 있어야 한다는 요건은 여전하다.

예제는 1장과 3장에서 본 소셜 네트워크 데이터를 사용한다(간단한 예제이지만 실제 임포트는 훨씬 큰 파일을 쓴다는 점을 유념하라). 먼저 도메인 모델을 그린다 — Person·Place 노드와 FRIEND·LIVES_IN 관계다. 왼쪽 패널에서 CSV 파일을 찾아 추가하고, 간단한 드로잉 도구로 노드·관계를 그린 다음, 오른쪽 매핑 상세 패널에서 각각에 주석을 단다.

Example 4-1 · people.csv (Person 노드)CSV
:ID(Person),name
23,Rosa
42,Karl
55,Fred
Example 4-2 · friends.csv (FRIEND 관계)CSV
:START_ID(Person),:END_ID(Person)
23,42
42,23
42,55
55,42
Example 4-3 · places.csv (Place 노드)CSV
:ID(Place),city,country
143,Berlin,Germany
244,London,UK
Example 4-4 · lives_in.csv (LIVES_IN + since)CSV
:START_ID(Person),:END_ID(Place),since
23,143,2020
55,244
42,244,1980

:ID(Person)은 노드의 ID와 레이블을 함께 지정하고, 이어지는 행이 그 패턴에 맞는 데이터다(예: 23,Rosa). ID 컬럼은 나중에 그래프를 이어 붙이는 데 쓰일 뿐, (반드시) 도메인 모델의 일부는 아니며 이후 모델에서 제거해도 된다. Example 4-4의 헤더는 관계의 시작을 Person ID, 끝을 Place ID로 지정하고 관계 위의 선택적 since 속성을 선언한다.

Data Importer는 라이브 데이터베이스를 대상으로 실행되므로 엔드포인트와 자격 증명이 필요하다. 성공적으로 실행되면 임포트 리포트를 받는데, 여기서 실행된 Cypher 스크립트와 임포트 통계를 볼 수 있다. 리포트에서 눈에 띄는 흔한 Cypher 관용구는 CREATE CONSTRAINT, UNWIND, MERGE다. UNWIND로 CSV 데이터 행을 MERGE 연산에 흘려보내 ID 속성을 가진 Person 노드를 만들고, SET으로 속성을 쓴다. 어떤 행에 since 속성이 없어도 문제없다 — SET은 데이터의 부재를 용인한다.

Note — UNWIND란

UNWIND는 값의 리스트를 받아 행(row)으로 변환해 후속 문장에 공급하는 Cypher 절이다. 입력 데이터를 다루는 매우 흔한 방법이다.

UNWIND [1, 2, 3, null] AS x
RETURN x   // 1, 2, 3, null이 개별 행으로 반환
Note — MERGE와 MATCH의 혼합

관계 데이터를 적재할 때는 MERGE와 MATCH의 조합이 쓰인다. Person·Place 노드는 입력 CSV에 존재가 보장되므로 MERGE하지 않고 MATCH만 한다. 그 대신 기존 노드들 사이의 LIVES_IN 관계만 MERGE하여 관계의 중복이 없음을 보장한다. 3장에서 배운 대로, MERGE는 레코드가 있으면 MATCH처럼, 없으면 CREATE처럼 동작하기 때문이다.

물론 동등한 Cypher를 손으로 써서 스크립트 집합으로 실행해도 같은 효과다(다음 절의 LOAD CSV가 그 방식이다). 생성된 Cypher를 자동 생성 소스로 취급해 수정하고 소스 컨트롤에 호스팅하면, 지식 그래프가 시간과 함께 성장할 때 코드도 함께 진화시킬 수 있다. Data Importer는 인제스천 Cypher 코드의 출발점을 줄 뿐 아니라, 임포트 시작 전에 모델과 유입될 데이터를 시각화·검증·디버깅하게 해 준다. 그래서 그래프 데이터베이스 입문자만이 아니라 숙련된 Neo4j 개발자에게도 가치 있다.

Online Bulk Loading

LOAD CSV — 라이브 데이터베이스로의 온라인 대량 적재

CSV 임포트를 스크립팅하는 또 다른 길이 Cypher의 LOAD CSV 명령이다. 다양한 위치에서 온라인 그래프로 CSV 데이터를 가져올 수 있다 — 웹 주소(Amazon S3 버킷, Google Sheets 등)와 파일시스템 모두 가능하며, 데이터베이스가 라이브 상태로 다른 질의를 서비스하는 동안 임포트가 진행된다. 유용하게도 CSV 파일은 압축될 수 있어 대량 전송에 도움이 된다.

Example 4-5 · Place 노드 CSVCSV
city,country
Berlin,Germany
London,UK
Example 4-6 · 단순 LOAD CSVCypher
LOAD CSV WITH HEADERS FROM 'places.csv' AS line
MERGE (:Place {country: line.country,
              city: line.city})

기본 패턴의 첫 줄은 이렇게 분해된다.

구성 요소의미
LOAD CSV실행하려는 Cypher 명령
WITH HEADERSCSV 첫 줄에 헤더 정의가 있다고 알린다. 선택 사항이지만 대개 쓰는 것이 좋다 — 컬럼을 숫자 인덱스가 아니라 친숙한 이름으로 참조할 수 있다
FROM 'places.csv'CSV 데이터의 위치. 파일시스템의 비기본 위치나 원격 서버에서 가져오려면 Neo4j 보안 권한 변경이 필요할 수 있다
AS lineCSV의 각 행을 Cypher 변수 line에 바인딩하여 이후 질의에서 접근

불규칙 데이터 — SET으로 null을 우회한다

사람 데이터는 훨씬 덜 규칙적이다. 모두에게 알려진 것은 이름뿐이고, 일부만 성별이나 나이를 안다. (현실 세계 그대로의) 불규칙 데이터에는 조금 더 정교한 접근이 필요하다. Example 4-6의 단순한 방식을 쓰면 질의가 null 속성 값을 불평하며 실패한다. SET을 사용해 노드에 null 속성을 쓰는 일을 피해야 한다.

Example 4-7 · 불규칙한 Person CSVCSV
name,gender,age
Rosa,f,
Karl,,64
Fred,,
Example 4-8 · SET을 쓰는 정교한 LOAD CSVCypher
LOAD CSV WITH HEADERS FROM 'people.csv' AS line
MERGE (p:Person {name: line.name})
SET p.age = line.age
SET p.gender = line.gender

마지막으로 LIVES_IN과 FRIEND 관계를 추가해 새 노드들을 지식 그래프로 잇는다(이미 지식 그래프가 있다면 기존 노드에 연결한다). Person 노드들이 이미 채워져 있음을 알고 있으므로, MATCH로 찾고 MERGE로 관계만 채운다.

Example 4-9 · FRIEND 관계 CSVCSV
from,to
Rosa,Karl
Karl,Rosa
Karl,Fred
Fred,Karl
Example 4-10 · FRIEND 관계 적재Cypher
LOAD CSV WITH HEADERS FROM "friend_rels.csv" AS line
MATCH (p1:Person {name:line.from}),
      (p2:Person {name:line.to})
MERGE (p1)-[:FRIEND]->(p2)
Note — 이름은 ID가 아니다

작은 예제라서 사람 이름이 ID 역할을 할 만큼 충분히 유일하다. 실제 임포트에서 이 가정은 성립하기 어렵고, 노드에 유일한 숫자 ID를 도입하는 것이 합리적이다. 실무적으로는 people·places CSV의 각 행에 증가하는 정수 ID를 추가하는 것을 뜻한다.

LIVES_IN 관계는 조금 더 까다롭다 — 선택적 since 속성이 있기 때문이다. Example 4-11의 Fred,London,, 행은 뒤에 붙은 이중 쉼표로 since 필드에 값이 없음을 나타낸다. MERGE (person)-[:LIVES_IN {since:line.since}]->(place)처럼 MERGE 안에서 속성을 지정하면 속성이 생략된 행에서 실패하므로 쓰지 않는다. 관계 레코드가 만들어진 뒤 SET으로 속성을 더한다. 문장은 트랜잭션으로 실행되므로, 문법상 분리되어 있어도 관계와 그 속성은 원자적으로 기록된다 — 전혀 문제없다.

Example 4-11 · LIVES_IN CSV (선택적 since)CSV
from,to,since
Rosa,Berlin,2020
Fred,London,,
Karl,London,1980
Example 4-12 · 속성 있는 관계 적재Cypher
LOAD CSV WITH HEADERS FROM "friend_rels.csv" AS line
MATCH (person:Person {name:line.from}),
      (place:Place {city:line.to})
MERGE (person)-[r:LIVES_IN]->(place)
SET r.since=line.since

배치 트랜잭션 — 대규모 임포트의 예의

100만 레코드 이상의 매우 큰 임포트에서는 작업을 더 작은 배치로 쪼개는 것이 합리적일 때가 많다. 대량 삽입으로 데이터베이스가 압도되는 것을 막고, 지식 그래프 위의 다른 질의들을 매끄럽게 유지한다. Neo4j 4.4 이전에는 APOC 라이브러리의 apoc.periodic.iterate를 썼고, 4.4부터는 Cypher에서 직접 CALL {...} IN TRANSACTIONS OF ... ROWS로 유사한 기능을 쓸 수 있다.

Example 4-13 · 배치 적재 (Neo4j Browser에서는 :auto 접두)Cypher
LOAD CSV WITH HEADERS FROM 'people.csv' AS line
CALL {
  WITH line
  MERGE (p:Person {name: line.name})
  SET p.age = line.age
  SET p.gender = line.gender
} IN TRANSACTIONS OF 1 ROWS   // 실제 시스템에서는 행 수와 배치 수 모두 훨씬 크다

LOAD CSV의 좋은 점은 그것이 보통의 Cypher라는 것이다. Cypher에 대해 배운 모든 것 — EXPLAINPROFILE을 포함해 — 을 대량 인제스천의 분석·디버깅· 튜닝에 그대로 쓸 수 있다. FRIEND 관계 적재의 질의 계획을 보면, Neo4j Browser가 경고하는 카테시안 곱(Cartesian product) 연산자조차 문제가 아니다 — 입력을 매칭되는 두 노드와 해당 CSV 행으로 줄여 주는 필터가 앞에 있기 때문이다. 물론 항상 그렇지는 않으므로, 소량의 대표 입력 데이터에 EXPLAIN·PROFILE을 돌려 성능 문제를 시작 전에 해결하는 것이 좋다.

Note — eager 연산자를 경계하라

카테시안 곱 외에도 질의 계획·프로파일에서 eager 연산자를 주시하라. eager 연산자는 모든 데이터를 즉시 끌어들여 병목 지점(choke point)을 만들곤 한다. eager 연산자 제거 전략에 대해서는 Mark Needham의 훌륭한 블로그 글이 있다.

LOAD CSV는 빠르고, 지식 그래프 수명주기의 어느 시점에나 쓸 수 있다. 갱신 중에도 데이터베이스는 온라인을 유지하며 다른 질의를 처리한다. 다만 일상적인 데이터베이스 연산을 지배하지 않으면서 좋은 처리량을 얻으려면 약간의 튜닝이 필요할 수 있다. 그리고 대량 삽입의 선택지는 이것이 마지막이 아니다 — 도구 상자에 하나가 더 남아 있다.

Initial Bulk Load

neo4j-admin import — 초기 부트스트랩의 특급 열차

지식 그래프를 잠재적으로 방대한 데이터로 부트스트랩하는 최초 임포트가 필요한 경우가 많다. Data Importer나 LOAD CSV로도 가능하지만, 다소 저수준일지언정 더 빠른 길이 있다. Neo4j 명령줄 도구 neo4j-admin에는 오프라인 임포터가 내장되어 있다. neo4j-admin import 명령은 CSV 파일 집합에서 새 Neo4j 데이터베이스를 구축한다.

이 도구는 데이터를 매우 빠르게 인제스트한다 — 초당 약 100만 레코드의 지속 성능 — 그리고 SSD와 SAN(storage area network) 같은 고처리량 장치에 대한 최적화를 갖추고 있다. 대량 임포트를 구축하는 고성능 방법이지만, 실행할 때마다 데이터베이스가 오프라인이어야 한다는 단서가 붙는다.

Note — 오프라인이라서 빠르다

neo4j-admin import의 단서는 오프라인 임포터라는 점이며, 그것이 이토록 빠른 이유다. 임포트가 끝나는 즉시 데이터베이스는 지식 그래프 질의 서비스 준비가 되지만, 구축 중에는 사용할 수 없다. Neo4j 5 이전에는 시스템의 초기 지식 그래프 생성에만 쓸 수 있었으나, 이후 제약이 완화되어 수명주기 전반에 걸친 복수의 오프라인 임포트가 허용된다.

Note — 파일시스템만, 그러나 완벽할 필요는 없다

데이터는 임포터가 접근 가능한 파일시스템에 있어야 한다(네트워크 마운트는 가능, S3 버킷 등 파일시스템이 아닌 저장소는 불가). 대용량을 위해 gzip 압축 CSV도 지원한다. CSV가 완벽할 필요는 없고 충분히 좋은 상태면 된다 — 임포터는 다른 구분자를 용인하고, 추가 컬럼을 무시하고, 중복과 불량 관계를 건너뛰고, 문자열 트리밍까지 해 준다. 다만 특수 문자, 누락된 따옴표, 보이지 않는 BOM(byte-order mark) 같은 것은 여전히 직접 확인해야 한다.

파일 배치 관행 — 헤더의 분리

임포터는 최소한 노드와 관계를 별도 파일로 기대한다. 나아가 노드·관계의 종류별로 CSV 파일을 나누는 것이 좋은 관행이다 — Person과 Place 노드, FRIEND와 LIVES_IN 관계를 분리하는 식이다. 매우 큰 파일은 여러 개의 작은 CSV로 쪼개고 헤더를 별도 파일로 두면, 헤더 수정 때문에 거대한 파일을 텍스트 에디터로 여는 고통이 사라진다. 임포터는 헤더 파일과 데이터 파일들을 — 파일이 몇 개든 — 논리적으로 단일한 단위로 취급한다.

Ex 4-15 · friends_header.csvCSV
:START_ID(Person),:END_ID(Person)
Ex 4-16 · friends1.csvCSV
23,42
42,23
Ex 4-17 · friends2.csvCSV
42,55
55,42
Ex 4-18 · places_header.csvCSV
:ID(Place), city, country
Ex 4-19 · places1.csvCSV
143,Berlin,Germany
Ex 4-20 · places2.csvCSV
244,London,UK
Ex 4-14 · people.csv (자기 완결형)CSV
:ID(Person),name
23,Rosa
42,Karl
55,Fred
Ex 4-21 · people_places.csv (선택적 since)CSV
:START_ID(Person),:END_ID(Place),since
23,143,2020
55,244
42,244,1980
Example 4-22 · neo4j-admin import 실행 (여러 줄 명령 — 줄 끝 \ 유의)bash
bin/neo4j-admin import --nodes=Person=import/people.csv \
--relationships=FRIEND=import/friends_header.csv,import/friends1.csv,\
    import/friends2.csv \
--nodes=Place=import/places_header.csv,import/places1.csv,import/places2.csv \
--relationships=LIVES_IN=import/people_places.csv
Caution — 재개 불가, Ctrl-C 금지

neo4j-admin import는 매우 빠르고 수십억 레코드의 초대형 데이터셋에 적합하다. 다만 큰 임포트에는 많은 RAM이 필요하고 그래도 수 시간이 걸릴 수 있음을 유념하라. 도구는 재개(resumable)가 불가능하므로, 실행 중 터미널 창에서 Ctrl-C를 누르지 않도록 조심하라 — 누르면 임포트를 처음부터 다시 시작해야 하며, 장시간 작업에서 이는 고통스럽다.

Summary

요약 — 수명주기에 맞는 도구의 선택

이제 온라인·오프라인 대량 임포트의 개념에 익숙해졌을 것이다. 각 기법이 어떻게 동작하는지 보았고 장단점도 숙고했다. 구축 중인 지식 그래프에 어떤 방법이 적절한지 — 수명주기 전반에 걸쳐 여러 도구를 함께 쓸 가능성까지 포함해 — 합리적으로 선택할 수 있어야 한다.

기준Data ImporterLOAD CSVneo4j-admin import
동작 모드온라인 (라이브 DB + 자격 증명)온라인 (서비스 중 적재)오프라인 (구축 중 사용 불가)
인터페이스시각적 모델링 + CSV 매핑보통의 Cypher (EXPLAIN·PROFILE 활용 가능)명령줄 (bin/neo4j-admin)
데이터 소스CSV 파일웹 주소(S3, Google Sheets)·파일시스템·압축 CSV파일시스템만(네트워크 마운트 가능)·gzip 지원
속도/규모소규모·부트스트랩빠름 · 배치 트랜잭션으로 대규모 대응~100만 레코드/초 · 수십억 레코드
적합한 국면입문·모델 검증·Cypher 출발점수명주기 내내 점진적 보강최초(및 오프라인 허용 시점의) 대량 부트스트랩
1

작게 시작하라. 대표성 있는 데이터 조각으로 모델을 검증한 뒤 전체 임포트를 돌려라. 빠른 점검 하나가 수 시간의 좌절을 아낀다.

2

Data Importer는 모델과 데이터를 임포트 전에 시각화·검증·디버깅하게 해 준다. 생성된 Cypher(CREATE CONSTRAINT·UNWIND·MERGE)는 소스 컨트롤에서 진화시킬 수 있는 출발점이다.

3

LOAD CSV의 정석 패턴. 존재가 보장된 노드는 MATCH, 관계만 MERGE, 선택적 속성은 SET으로(트랜잭션이 원자성을 보장). 이름 대신 유일 숫자 ID를 도입하고, 대규모에서는 CALL {} IN TRANSACTIONS로 배치화하라.

4

질의 계획을 읽어라. 카테시안 곱은 필터가 앞에 있으면 무해할 수 있지만, eager 연산자는 병목을 만든다. 소량 데이터로 EXPLAIN·PROFILE을 먼저 돌려라.

5

neo4j-admin import는 오프라인이라서 빠르다. 헤더 분리·종류별 파일 분할 관행을 따르고, CSV는 "충분히 좋은" 상태면 되지만 특수 문자·BOM은 직접 챙겨라. 재개 불가 — Ctrl-C 금지.

Next — 5장 예고

다음으로 스택을 한 층 더 올라간다. 지식 그래프를 지속적으로 최신 상태로 유지하고 후속 처리를 트리거하기 위해 데이터를 지식 그래프로 스트리밍하는 방법 — 즉 지식 그래프와 정보 시스템의 통합(5장, Integrating Knowledge Graphs with Information Systems)을 탐구한다.