Dgraph
Distributed Graph Database
GraphQL 네이티브 분산 그래프 데이터베이스입니다. 수평 확장, ACID 트랜잭션, 실시간 구독을 지원하며 대규모 연결 데이터 처리에 최적화되어 있습니다.
Distributed Graph Database
GraphQL 네이티브 분산 그래프 데이터베이스입니다. 수평 확장, ACID 트랜잭션, 실시간 구독을 지원하며 대규모 연결 데이터 처리에 최적화되어 있습니다.
Dgraph는 Go 언어로 개발된 오픈소스 분산 그래프 데이터베이스입니다. Google 출신 엔지니어들이 만들었으며, 처음부터 분산 시스템으로 설계되어 페타바이트 규모의 그래프 데이터를 처리할 수 있습니다.
GraphQL 네이티브 지원이 핵심 특징입니다. 스키마를 GraphQL로 정의하면 자동으로 CRUD API가 생성됩니다. 별도의 GraphQL 서버 구축 없이 데이터베이스에서 직접 GraphQL 엔드포인트를 제공합니다.
DQL(Dgraph Query Language)은 GraphQL을 확장한 쿼리 언어로, 그래프 순회, 집계, 필터링에 최적화되어 있습니다. 재귀 쿼리와 복잡한 조인을 효율적으로 처리하며, 정규 표현식, 지리공간 쿼리, 전문 검색도 지원합니다.
분산 아키텍처는 Zero(클러스터 조정), Alpha(데이터 저장/쿼리), Ratel(UI)로 구성됩니다. 데이터는 자동으로 샤딩되고 복제되며, Raft 합의 알고리즘으로 일관성을 보장합니다. 노드 추가만으로 선형적인 성능 확장이 가능합니다.
소셜 네트워크, 추천 엔진, 지식 그래프, 사기 탐지 등 관계 중심 데이터 분석에 적합합니다.
# Dgraph Python 클라이언트 (pydgraph)
import pydgraph
import json
# 클라이언트 연결
client_stub = pydgraph.DgraphClientStub('localhost:9080')
client = pydgraph.DgraphClient(client_stub)
def set_schema():
"""GraphQL 스키마 설정"""
schema = """
type Person {
name: string @index(exact, term) .
age: int @index(int) .
email: string @index(exact) @upsert .
friends: [Person] @reverse .
works_at: Company .
}
type Company {
name: string @index(exact) .
industry: string @index(term) .
employees: [Person] @reverse .
}
"""
op = pydgraph.Operation(schema=schema)
client.alter(op)
print("스키마 설정 완료")
def create_data():
"""데이터 삽입"""
txn = client.txn()
try:
# JSON 형식으로 데이터 생성
data = {
"uid": "_:alice",
"dgraph.type": "Person",
"name": "Alice",
"age": 28,
"email": "alice@example.com",
"friends": [
{
"uid": "_:bob",
"dgraph.type": "Person",
"name": "Bob",
"age": 32,
"email": "bob@example.com"
},
{
"uid": "_:charlie",
"dgraph.type": "Person",
"name": "Charlie",
"age": 25,
"email": "charlie@example.com"
}
],
"works_at": {
"uid": "_:techcorp",
"dgraph.type": "Company",
"name": "TechCorp",
"industry": "Technology"
}
}
response = txn.mutate(set_obj=data)
txn.commit()
# 생성된 UID 확인
print("생성된 노드:")
for key, uid in response.uids.items():
print(f" {key}: {uid}")
return response.uids
finally:
txn.discard()
def query_friends():
"""친구 관계 쿼리 (DQL)"""
query = """
{
person(func: eq(name, "Alice")) {
uid
name
age
friends {
name
age
email
}
works_at {
name
industry
}
}
}
"""
txn = client.txn(read_only=True)
try:
response = txn.query(query)
result = json.loads(response.json)
print("\nAlice의 정보:")
for person in result.get('person', []):
print(f" 이름: {person['name']}, 나이: {person['age']}")
print(f" 회사: {person.get('works_at', {}).get('name', 'N/A')}")
print(" 친구들:")
for friend in person.get('friends', []):
print(f" - {friend['name']} ({friend['age']}세)")
finally:
txn.discard()
def query_with_variables():
"""변수를 사용한 쿼리"""
query = """
query findPerson($name: string, $minAge: int) {
person(func: eq(name, $name)) @filter(ge(age, $minAge)) {
uid
name
age
friends @filter(ge(age, $minAge)) {
name
age
}
}
}
"""
variables = {"$name": "Alice", "$minAge": "25"}
txn = client.txn(read_only=True)
try:
response = txn.query(query, variables=variables)
result = json.loads(response.json)
print(f"\n쿼리 결과: {json.dumps(result, indent=2, ensure_ascii=False)}")
finally:
txn.discard()
def upsert_example():
"""Upsert (있으면 업데이트, 없으면 삽입)"""
query = """
{
user as var(func: eq(email, "alice@example.com"))
}
"""
mutation = """
uid(user) "29" .
uid(user) "%s" .
""" % "2024-01-15T10:30:00Z"
txn = client.txn()
try:
request = pydgraph.Request(
query=query,
mutations=[pydgraph.Mutation(set_nquads=mutation)],
commit_now=True
)
response = client.txn().do_request(request)
print("\nUpsert 완료")
finally:
txn.discard()
# 실행
if __name__ == "__main__":
set_schema()
uids = create_data()
query_friends()
query_with_variables()
upsert_example()
# 연결 종료
client_stub.close()
// Dgraph Node.js 클라이언트 (dgraph-js)
const dgraph = require('dgraph-js');
const grpc = require('@grpc/grpc-js');
// 클라이언트 연결
const clientStub = new dgraph.DgraphClientStub(
'localhost:9080',
grpc.credentials.createInsecure()
);
const dgraphClient = new dgraph.DgraphClient(clientStub);
async function setSchema() {
const schema = `
type Person {
name: string @index(exact, term) .
age: int @index(int) .
email: string @index(exact) @upsert .
friends: [Person] @reverse .
}
`;
const op = new dgraph.Operation();
op.setSchema(schema);
await dgraphClient.alter(op);
console.log('스키마 설정 완료');
}
async function createData() {
const txn = dgraphClient.newTxn();
try {
const data = {
uid: '_:alice',
'dgraph.type': 'Person',
name: 'Alice',
age: 28,
email: 'alice@example.com',
friends: [
{
uid: '_:bob',
'dgraph.type': 'Person',
name: 'Bob',
age: 32,
email: 'bob@example.com'
}
]
};
const mu = new dgraph.Mutation();
mu.setSetJson(data);
mu.setCommitNow(true);
const response = await txn.mutate(mu);
console.log('생성된 UID:', response.getUidsMap().toObject());
return response.getUidsMap();
} finally {
await txn.discard();
}
}
async function queryData() {
const query = `
{
people(func: has(name), orderasc: name) {
uid
name
age
email
friends {
name
age
}
}
}
`;
const txn = dgraphClient.newTxn({ readOnly: true });
try {
const response = await txn.query(query);
const result = response.getJson();
console.log('\n전체 사용자:');
result.people.forEach(person => {
console.log(` ${person.name} (${person.age}세)`);
if (person.friends) {
person.friends.forEach(friend => {
console.log(` → ${friend.name}`);
});
}
});
} finally {
await txn.discard();
}
}
async function graphqlMutation() {
// GraphQL 엔드포인트 사용 (HTTP)
const fetch = require('node-fetch');
const mutation = `
mutation AddPerson($input: AddPersonInput!) {
addPerson(input: [$input]) {
person {
uid
name
age
}
}
}
`;
const variables = {
input: {
name: 'David',
age: 35,
email: 'david@example.com'
}
};
const response = await fetch('http://localhost:8080/graphql', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query: mutation, variables })
});
const result = await response.json();
console.log('\nGraphQL 뮤테이션 결과:', result);
}
async function subscriptionExample() {
// 실시간 구독 (WebSocket)
const { SubscriptionClient } = require('subscriptions-transport-ws');
const ws = require('ws');
const client = new SubscriptionClient(
'ws://localhost:8080/graphql',
{ reconnect: true },
ws
);
const subscription = `
subscription WatchPeople {
queryPerson {
name
age
}
}
`;
client.request({ query: subscription }).subscribe({
next: data => console.log('변경 감지:', data),
error: err => console.error('구독 오류:', err)
});
}
// 실행
(async () => {
await setSchema();
await createData();
await queryData();
await graphqlMutation();
clientStub.close();
})();
# ============================================
# GraphQL 스키마 정의
# ============================================
type Person {
id: ID!
name: String! @search(by: [exact, term])
age: Int @search(by: [int])
email: String! @id # 유니크 식별자
friends: [Person] @hasInverse(field: friends)
posts: [Post] @hasInverse(field: author)
}
type Post {
id: ID!
title: String! @search(by: [fulltext])
content: String
author: Person!
tags: [String] @search(by: [exact])
createdAt: DateTime!
}
# ============================================
# GraphQL 쿼리 예제
# ============================================
# 모든 사용자와 친구 조회
query {
queryPerson {
name
age
friends {
name
}
}
}
# 필터링과 페이지네이션
query {
queryPerson(
filter: { age: { ge: 25 } }
order: { asc: name }
first: 10
offset: 0
) {
name
age
email
}
}
# ============================================
# DQL (Dgraph Query Language) 예제
# ============================================
# 기본 쿼리
{
people(func: type(Person), orderasc: name, first: 10) {
uid
name
age
email
}
}
# 그래프 순회 - 친구의 친구 찾기
{
alice(func: eq(name, "Alice")) {
name
friends {
name
friends {
name
}
}
}
}
# 재귀 쿼리 - N단계 관계 탐색
{
connections(func: eq(name, "Alice")) @recurse(depth: 3) {
name
friends
}
}
# 필터와 집계
{
stats(func: type(Person)) @filter(ge(age, 25)) {
count(uid)
avg_age: avg(age)
min_age: min(age)
max_age: max(age)
}
}
# 전문 검색
{
search(func: alloftext(content, "machine learning AI")) {
title
content
author {
name
}
}
}
# 지리공간 쿼리
{
nearby(func: near(location, [37.5665, 126.9780], 5000)) {
name
location
}
}
# ============================================
# 뮤테이션 예제
# ============================================
# GraphQL 뮤테이션
mutation {
addPerson(input: [{
name: "Eve"
age: 30
email: "eve@example.com"
friends: { name: "Alice" }
}]) {
person {
id
name
}
}
}
# DQL 뮤테이션 (RDF 형식)
{
set {
_:eve "Person" .
_:eve "Eve" .
_:eve "30"^^ .
_:eve "eve@example.com" .
}
}
"소셜 기능에 Dgraph를 추천합니다. GraphQL 스키마만 정의하면 API가 자동 생성되니까 개발 속도가 빨라요. 친구 추천 같은 그래프 쿼리도 DQL로 재귀 탐색이 가능해서 복잡한 관계 분석에 유리합니다."
"Dgraph는 처음부터 분산 설계라 스케일 아웃이 자연스럽습니다. Alpha 노드만 추가하면 되고, 샤딩과 복제가 자동으로 처리돼요. 10억 노드 이상 그래프도 처리 가능합니다. 다만 운영 복잡도가 있으니 클라우드 매니지드 옵션도 고려해 보세요."
"Neo4j는 Cypher 쿼리와 성숙한 생태계가 장점이고, Dgraph는 GraphQL 네이티브와 수평 확장이 강점입니다. 프론트엔드에서 GraphQL을 이미 쓰고 있다면 Dgraph가 통합하기 편하고, 분석 중심이면 Neo4j가 더 익숙할 수 있어요."
DQL은 GraphQL과 유사하지만 별도 문법입니다. 팀 전체가 새로운 쿼리 언어를 학습해야 하는 비용을 고려하세요.
Zero, Alpha, Ratel 등 여러 컴포넌트를 관리해야 합니다. 소규모 프로젝트에는 오버헤드가 클 수 있어요.
그래프 데이터를 메모리에 캐싱하므로 대규모 데이터셋은 충분한 RAM이 필요합니다. 노드당 최소 16GB 이상 권장합니다.
인덱스를 적절히 설정하고(@index), @reverse로 역방향 엣지를 활용하세요. 쿼리에 first/offset으로 페이지네이션하고, 깊은 재귀는 depth 제한을 두세요.