Seongsiks

Being A DevOpser. Powered by
Obtvse, highlight.js, theme toc Creative Commons License
Seongsiks Twitter Github Email
DevOps Ruby On Rails Chef Projects Misc Movies & Drama ME

Neo4J - 그래프 데이터베이스

Overview

nosql에는 4종류가 있다.

  • Key-Value Store(DynamoDB)
  • Column-Family Store(Cassandra, hbase)
  • Document Database(MongoDB)
  • Graph Database(Neo4j)

위 네가지 타입중, Graph Database에 속하는 Neo4J는 각 노드간에 관계를 정의하는 다른 요소를 가지고 있다. 이를 사용함으로써 얻을 수 있는 장점으로는 RDMS에서 겪는 join pain을 제거 할 수 있으며, 다른 NoSQL에서도 하기 어려운 관계중심 쿼리를 이용하여 쉽게 연관된 데이터를 뽑아 올 수 있다.

Licenses

참고: Emil Eifrem about Neo4j 1.3 and the Neo4j GPL Community Edition

Neo4J는 3가지 라이센스를 가지고 있는데 그 용도에 따라서 약간의 기능 차이가 있으나 기본 Community버전에서도 모든 기능을 다 사용할 수 있다. 자세한 사항은 다음과 같다.

Community

MySQL처럼 자유롭게 사용할 수 있다. 일단 GPL라이센스이어서 어플리케이션에 임베디드할 경우가 아니면 소스 공개 의무는 없다.

Advanced

모니터링과 운영 지원이 포함되어 있다. 이 버전에서는 AGPL 라이센스를 따르던가, 상용 라이센스를 구매해야한다.

Enterprise

auto replication과 전체적인 모니터링을 지원하며, 사용 24시간 운영 지원이 포함되어 있다. 이를 원하지 않을 경우 AGPL 라이센스로 사용가능하다.

그러나 Community를 제외한 다른 버전을 다운받으려고 하면 개인정보를 입력해야한다. 또 각 언어별 클라이언트에 라이센스도 확인해야한다. 일단 ruby 클라이언트 자체는 MIT라이센스이고 자바는 GPL 이다. 더 자세한 라이센스는 사용이 확정되면 쓰면 될듯하다. 그러나 이런 라이센스가 크게 문제가 안될 수 있다. 왜냐면 이런 전용 클라이언트 말고도 REST API을 제공하기때문에 기본적인 오퍼레이션은 이를 사용하면 될 것으로 본다.

모델링

그래프 데이터베이스 Neo4J는 기존 RDMS는 물론 다른 어떤 noSQL도 관계자체를 데이터 객체로 다루지 않아 관계를 다루는 적합하지 않다고 말하고 있다.

장점

  1. RDMS와 같이 CRUD를 제공한다.
  2. 쿼리 실행시간은 전체 그래프의 크기가 아니라 쿼리를 통해 연산되는 그래프의 크기에 영향을 받는다.
  3. 그래프 모델 자체는 Additive의 특성을 가진다. 즉, 정해져있는 스키마가 존재하지 않으며 기존 그래프에 관계나 노드를 계속해서 추가하는 방식을 따르게 된다.

속성

  1. 그래프는 node, relationship, property로 구성된다.
  2. node와 relationship은 property를 가진다.
  3. relationship은 node를 서로 연결하며, Label방향을 가지고 있다. 따라서, start node와 end node를 가진다.
  4. relationship의 property는 추가적인 메타데이터(그래프 알고리즘, 가중치, 중복 관계등..)를 담는데 적합하다.
  5. Cypher라는 전용 언어를 사용한다.

주의점

  • 영어문장으로 왼쪽에서 오른쪽으로 읽히도록 설계한다.

    |seongsik| -[:USEROF]-> |DAUMAPP| -[:USES]-> |mysql|

  • 예제에서 relationship의 이름을 할때 of를 많이 사용하는데, 우리나라 말로 "의"로 해석되나 순서가 뒤집힌다.

    Game of Thrones => 왕좌의 게임, 게임의 왕좌X

  • 관계를 정의 할때 emailed라는 관계를 사용하게 되면 안된다. 그 이유는 이 동사는 email이라는 node개념을 삼키고 있는 동사여서 email이라는 node를 설계에 넣지 않고 진행할 가능성이 크기 때문이다.

  • 그래프 데이터베이스는 additive한 속성을 가지기때문에 추가에대해서 자유로우나 기존것을 바꾸는것은 다른 데이터베이스와 같이 어려운 작업이다.

모델링하기

  1. Entity를 표현하기 위해서는 node를 사용하라.
  2. 각각의 entity의 관계를 설명하기 위해서 relationship을 사용해라 이로써 데이터 구조가 구성된다.
  3. relationship의 방향은 관계를 좀 더 명확하게 하는데 사용하면 된다. 보통 거의 모든 경우에 관계는 일방적이지만, 양방향성의 관계가 존재할때는 쿼리를 만들때 방향을 무시하도록 작성하면 된다.
  4. node property는 해당 entity의 속성을 설명하는데 사용하고 타임스탬프나 버전정보같은 메타데이터를 저장하는데 사용하는것이 바람직하다.
  5. relationship property는 강도, 가중치, 양을 정의하는데 용이하다.

각종 모델링 예제 링크

Neo4J 사용 방법

Embedded Mode

장점
  • Low Latency : 데이터베이스와 바로 통신하기 때문에 네트워크 지연이 없음
  • Choice of APIs** : Core API, traversal framework등 각종 API를 선택할 수 있음
  • Explicit Transaction Control : Core API를 이용하여 좀더 복잡하고 섬세한 작업을 하나의 트랜젝션으로 묶어서 실행 시킬 수 있음
  • Named Index : 이 모드는 모든 컨트롤을 할 수 있기때문에 node와 relationship을 인덱싱 할 수 있다.(REST API로도 가능, Cypher 불가)
단점
  • JVM only : Neo4J가 자바로 작성되었기 때문에 jvm에서 실행되는 언어로만 개발해야함
  • GC Behaviors : 어플리케이션이 GC을 할경우 데이터베이스 자체도 같이 멈춤, 반대로 클러스터모드로 사용할 때, master 재지정이 발생했을때 어플리케이션도 같이 멈춤
  • Database Life Cycle : 어플리케이션과 데이터베이스가 하나로 합쳐져 있으므로 데이터베이의 시작과 종료와 같은 라이프 사이클도 어플리케이션이 직접 관리해줘야함

Server Mode

가장 일반적인 사용법

장점
  • REST API : JSON으로 작성된 요청과 응답으로 사용가능, Cypher에서 제공하지 않는 추가 기능 제공
  • Platform Independence : 당연히 다양한 언어로 준비된 클라이언트를 이용하여 플랫폼에 상관없이 어플리케이션을 작성할 수 있음
  • Scaling Independence : 당연당연..
  • Isolation from application GC Behavior : 당연당연..
단점
  • Network Overhead
  • Per-request : 기본적으로 클라이언트 요청당 하나의 트랜젝션으로 처리(보완-server extension mode)

Server Extension Mode

Annotation 기반으로 REST API를 새로 작성하거나 추가할 수 있는 모드.

@Path("/distance")
public class SocialNetworkExtension {
    private final ExecutionEngine executionEngine;

    public SocialNetworkExtension( @Context GraphDatabaseService db ) {
        this.executionEngine = new ExecutionEngine( db ); 
    }

    @GET
    @Produces("text/plain")
    @Path("/{name1}/{name2}")
    public String getDistance ( @PathParam("name1") String name1,
                                     @PathParam("name2") String name2 )
    {
        String query = "START first=node:user(name={name1}),\n" +
                    "second=node:user(name={name2})\n" +
                    "MATCH p=shortestPath(first-[*..4]-second)\n" +
                    "RETURN length(p) AS depth";
        Map<String, Object> params = new HashMap<String, Object>();
        params.put( "name1", name1 );
        params.put( "name2", name2 );

        ExecutionResult result = executionEngine.execute( query, params );
        return String.valueOf( result.columnAs( "depth" ).next() );
    }
}

Cypher

Neo4J 전용 쿼리 언어로 기존 쿼리문과 많이 닮아 있다.

Create

// 노드 만들기
CREATE n
CREATE ({name:'seongsik', team:"Tech Asset"})

// 관계만들기
START a=node(1), b=node(2)
CREATE a-[r:RELTYPE {name: a.name, team: b.team}]->b
RETURN r

// 패스 만들기
CREATE p = (seongsik {name:'Seong Sik'})-[:WORKS_AT]->daum<-[:WORKS_AT]-(michael {name:'Michael'})
RETURN p

Read

START
// 아이디로 찾기
START n=node(1,3), r=relationship(0)
RETURN n, r

// 모두 찾기
START n=node(*), r=relationship(*)
RETURN n, r

// 인덱스로 찾기
START n=node:index-name(key = "value"), r=relationship:index-name(key = "value")
RETURN n, r
// Cypher로 바로 인덱스를 거는것은 1.9.3 안정화 버전에서 아직 지원하지 않음.

// 인덱스 쿼리로 찾기, 쿼리는 Lucene 쿼리를 사용함
START n=node:index-name("name:A"), r=relationship:index-name("name:B")
RETURN n
MATCH
// 관계되어 있는 모든 노드
START n=node(3)
MATCH (n)--(x)
RETURN x

// 특정 노드에서 특정 방향에 따른 매치
START n=node(3)
MATCH (n)-[r]->(x)
RETURN x
//or RETURN r
// x와 r을 같이 리턴하게 되면 row가 많은 쪽으로 맞춰서 리턴된다.

// 관계 타입에 따른 매칭
START n=node(3)
// n과 특정 관계를 가진 노드를 x에 대입, 관계들을 r에 대입
MATCH (n)-[r:BLOCKS]->(x)
RETURN x //or RETURN r

// 여러 관계 타입에 따른 매칭
START n=node(3)
MATCH (n)-[r:BLOCKS|KNOWS]->(x)
RETURN x //or RETURN r

// a 노드에서 x노드들까지 KNOWS관계가 1~3 뎁스인 패스 다 구하기
START a=node(3), x=node(2, 4)
MATCH p = a-[:KNOWS*1..3]->x
RETURN p

// 가장 짧은 패스 구하기
START d=node(1), e=node(2)
MATCH p = shortestPath( d-[*..15]->e )
RETURN p

// 가장 짧은 패스가 한개 이상이면 다 리턴하기
START d=node(1), e=node(2)
MATCH p = allShortestPaths( d-[*..15]->e )
RETURN p

// 관계의 양끝에 있는 노드 찾기
START r=rel(0)
MATCH a-[r]-b
RETURN a,b
WHERE

Where 참고

START n=node(3, 1)
WHERE (n.age < 30 and n.name = "Tobias") or not(n.name = "Tobias")
RETURN n

// 정규식 - 일반적인 정규식이 아니어서 약간 확인하고 사용해야함.
START n=node(3, 1)
WHERE n.name =~ 'Tob.*'
RETURN n

// 속성 존재 여부 확인
START n=node(3, 1)
WHERE has(n.belt)
RETURN n

// 속성에 ?는 해당 속성이 존재하지 않아도 조건식을 true로 판정하고
// !는 해당 속성이 존재하지 않을때 false로 판정하도록 한다. 
START n=node(3, 1)
WHERE (n.belt? = 'white') or (n.name! = 'seongsik')
RETURN n

// IN 반복자
START a=node(3, 1, 2)
WHERE a.name IN ["Peter", "Tobias"]
RETURN a
RETURN
// 쿼리상에 있는 모든 엔티티 다 리턴 
START a=node(1)
MATCH p=a-[r]->b
RETURN *

START a=node(1)
RETURN a.age AS SomethingTotallyDifferent

// 리턴절에는 조건, 리터럴, 함수, 패턴등 거의 대부분을 들어갈수 있습니다. 
START a=node(1)
RETURN a.age > 30, "I'm a literal", length(a-->())


//중복된 결과 제거
START a=node(1)
MATCH (a)-->(b)
RETURN distinct b

Update

// 기존 property 값 변경하기
START n = node(2)
SET n.surname = 'Taylor'
RETURN n

// 기존 property 삭제하기
START n = node(2)
SET n.name = null
RETURN n

// 복사하기
START at = node(2), pn = node(1)
SET at = pn
RETURN at, pn

Delete

// 노드 삭제하기
START n = node(4)
DELETE n

// 노드와 그와 연결된 모든 관계삭제하기
START n = node(3)
//()는 익명 노드, -[]-에 <,>가 없으면 모든 방향
// 결국 n으로 시작하거나 끝나는 모든 관계는 r에 대입됨
MATCH n-[r]-() 
DELETE n, r

// property삭제
START andres = node(3)
DELETE andres.age
RETURN andres

Clustering

cluster 클러스터를 구성하기 위해서는 Enterprise버전의 Neo4J가 필요합니다. HA모드로 사용할때는 항상 마스터 슬레이브로 동작하게 됩니다. 그리고 마스터가 내려가게 되면 남아있는 슬레이브들이 마스터를 재선출하게 되는 구조입니다. 쓰기는 다른 마스터/슬레이브와 다르게 슬레이브에서도 가능하지만, consistency를 보장하기 위해서 마스터에 먼저 쓰고 이를 다른 슬레이브에 전달하게 됩니다. 때문에 마스터와 슬레이브에 동기화에 약간의 시간차가 생길 수 있습니다.

설정 파일 예제

Neo4j instance #1 — neo4j-01.local

conf/neo4j.properties:

# Unique server id for this Neo4j instance
# can not be negative id and must be unique
ha.server_id = 1

# List of other known instances in this cluster
ha.initial_hosts = neo4j-01.local:5001,neo4j-02.local:5001,neo4j-03.local:5001
# Alternatively, use IP addresses:
#ha.initial_hosts = 192.168.0.20:5001,192.168.0.21:5001,192.168.0.22:5001

conf/neo4j-server.properties

# Let the webserver only listen on the specified IP.
org.neo4j.server.webserver.address=0.0.0.0

# HA - High Availability
# SINGLE - Single mode, default.
org.neo4j.server.database.mode=HA

Neo4j instance #2 — neo4j-02.local

conf/neo4j.properties:

# Unique server id for this Neo4j instance
# can not be negative id and must be unique
ha.server_id = 2

# List of other known instances in this cluster
ha.initial_hosts = neo4j-01.local:5001,neo4j-02.local:5001,neo4j-03.local:5001
# Alternatively, use IP addresses:
#ha.initial_hosts = 192.168.0.20:5001,192.168.0.21:5001,192.168.0.22:5001
conf/neo4j-server.properties

# Let the webserver only listen on the specified IP.
org.neo4j.server.webserver.address=0.0.0.0

# HA - High Availability
# SINGLE - Single mode, default.
org.neo4j.server.database.mode=HA

Neo4j instance #3 — neo4j-03.local

conf/neo4j.properties:

# Unique server id for this Neo4j instance
# can not be negative id and must be unique
ha.server_id = 3

# List of other known instances in this cluster
ha.initial_hosts = neo4j-01.local:5001,neo4j-02.local:5001,neo4j-03.local:5001
# Alternatively, use IP addresses:
#ha.initial_hosts = 192.168.0.20:5001,192.168.0.21:5001,192.168.0.22:5001
conf/neo4j-server.properties

# Let the webserver only listen on the specified IP.
org.neo4j.server.webserver.address=0.0.0.0

# HA - High Availability
# SINGLE - Single mode, default.
org.neo4j.server.database.mode=HA

Start the Neo4j servers as normal.(순서는 상관없음)

neo4j-01$ ./bin/neo4j start
neo4j-02$ ./bin/neo4j start
neo4j-03$ ./bin/neo4j start

또다른 방법

# ha.initial_hosts

이 옵션을 이용하여 최소한 한개이상의 클러스터 맴버을 설정하면 해당 맴버에 다른 맴버를 물어봐서 클러스터에 조인하는 방법을 사용할 수도 있다. (ha.discovery.enabled은 false로 설정해야함.)

# ha.discovery.url

이 옵션에 지정한 url로 접속하면 클러스터 맴버 정보를 받을 수 있게 하여 다른 맴버를 알아낼 수 있게 할 수 있다. (ha.discovery.enabled은 true로 설정해야함.)

Updating Version 2.0

label: 아직은 label을 통한 검색은 지원하고 있지 않지만, 현재 마일스톤5인 2.0버전에서는 이 기능이 들어가 있다. 현재 안정화 버전인 1.9.3에서는 index를 통한 그룹핑만 가능하다.

index: Cypher자체로 인덱스를 만드는 기능이 아직 없다.(2.0에서 도입예정) lucene을 이용해서 인덱스를 걸어줘야 함.. 헐..

REFERENCE

comments powered by Disqus
Back to Misc