Neo4j Hello world~!!
인사이드를 살펴보기전에 Neo4j 의 사용 예를 간단하게 체크하자.
- Relationship 타입을 enum 으로 만든다.
private static enum RelTypes implements RelationshipType
{
KNOWS // 아는 관계 ( 친구 , 가족 뭐 그런..)
}
- 관련 변수 선언
GraphDatabaseService graphDb;
Node firstNode;
Node secondNode;
Relationship relationship;
- 데이터 베이스 시작
graphDb = new GraphDatabaseFactory().newEmbeddedDatabase( DB_PATH );
registerShutdownHook( graphDb );
- 트랜잭션으로 todo list 감싸기
try ( Transaction tx = graphDb.beginTx() )
{
tx.success();
}
- 작은 그래프 만들어보자.
firstNode = graphDb.createNode();
firstNode.setProperty( "message", "Hello, " );
secondNode = graphDb.createNode();
secondNode.setProperty( "message", "World!" );
relationship = firstNode.createRelationshipTo( secondNode, RelTypes.KNOWS );
relationship.setProperty( "message", "brave Neo4j " );

- 그래프를 출력해보고
System.out.print( firstNode.getProperty( "message" ) );
System.out.print( relationship.getProperty( "message" ) );
System.out.print( secondNode.getProperty( "message" ) );
- 그래프 데이터를 삭제해보자
firstNode.getSingleRelationship( RelTypes.KNOWS, Direction.OUTGOING ).delete();
firstNode.delete();
secondNode.delete();
위의 소스를 보면 중요한 3가지 특성이 있다는것을 알수있다.
1. 노드 (firstNode, SecondNode) : '노드 속성을 가지고있다'
2. 관계 (relationship) : '관계 타입, 노드 속성 , 노드 방향을 가지고있다.'
3. 속성 ( 노드속성 : "hello" , 'world' / 관계 속성 : "brave neo4j")
이제 이정도만 기억해두고 이런 정보들을 과연 파일에 어떻게 저장하는지에 관해 알아보자.
Neo4j Internals: File Storage
1.저장되는 파일 이름들
- 노드 .db 파일 (neostore.nodestore.db)
- 관계 .db 파일 (neostore.relationshipstore.db)
- 노드 .id 파일 (neostore.nodestore.db.id)
- .propertystore 파일 (neostore.propertystore.db)
- .index 파일 (neostore.propertystore.db.index)
2. 재 사용되는 Ids
neo 의 데이터가 동일한 사이즈의 청크로 저장된다고 가정해보자. 이걸 "chunks records" 라고 말한다고 치고,
각각 레코드들은 그것과 연계된 id 를 가지고있다고하자. 파일안에서 데이터를 어떻게 찾을수있을까?
만약 데이타베이스에 대해서 좀 알고있다면 , 다양한 파일 엑세스 기법들에 대해서 떠오를것이다.
제일 쉬운방법은 id 를 자동적으로 하나씩 증가하게하면, 파일내에서 id 의 위치는 id * 레코드사이즈가 될것이다.
이때 문제가 하나 생기는데, 중간에 id 들이 삭제되면 어떻게 될냐는건데, 새로운 노드를 그 중간에 붕 뜬 자리에
넣어주면되는데, 위에서 말한바와 같이 id 와 파일에서의 offset 은 id * 레코드사이즈라는 관계가 있다는 문제가 있다.
그래서 이걸 해결하기위한 방법은 지워진 id 를 기억해두었다가, 새로운 레코드에 적용시켜주면 되는것이다.
결국 id 를 요청하면 , 서버는 재사용되는 id 가 있으면 할당해주고, 없으면 새로운 id 를 만들어주는 전력을 취하면된다.
- neo 서버가 영원히 메모리위에 있을수가 없기때문에, 저런 재사용 ids 를 파일에 저장해두어야한다.
- org.neo4j.kernel.impl.nioneo.store.IdGenerator 와 이것의 구현체인 IdGeneratorImpl 클래스가 사용된다.
- 모든 데이터파일의 관리자는 IdGenerator 를 유지해야한다.
- "id" 파일은 ".db" 파일과 나란히 연결되있다.
- 위에 헬로월드를 실행시키고 (삭제하기전에) neostore.nodestore.db.id 파일을 살펴보면 , 9바이트를 볼수있는데
처음 0 은 클린 셧다운을 말하고, 나머지 8 바이트는 3 의 long 타입이다. 이 3은 다음 사용될 id 를 나타낸다.
1과 2는 이미 사용되고있다. 지워진 노드가 없기때문에 재사용 ids 도 물론 없다.
4. Neo4j 의 두가지 저장법.
가.org.neo4j.kernel.impl.nioneo.store.AbstractStore
- 고정된 사이즈의 레코드를 다룬다.
나. org.neo4j.kernel.impl.nioneo.store.AbstractDynamicStore
- 다양한 사이즈의 레코드를 블럭안에 저장한다. 그리고 LinkedList 같이 디스크상에서 연결해서 저장한다.
- 두가지 모두 그들의 빌딩블록들과 연결된 ids 를 사용한다. 차이점은 AbstractStore 는 바깥 세상과 ids 를 가지고
소통한다는 점이고, DynamicStore 의 ids 는 오직 클래스안에서 의미를 가진다는점이다.
Record - oriented storage : The primitives
5. 노드 스토어
- org.neo4j.kernel.impl.nioneo.store.NodeStore 는 neostore.nodestore.db 파일을 만든다.
- 모든 노드는 여기에 저장된다.
- 간단하게 9 바이트의 고정 레코드를 살펴보면, 첫바이트는 사용 플레그이고, 그 다음 4바이트 integer 는
relationships 의 첫번째 id 이고, 마지막 4 byte 는 이 노드가 가지고있는 첫번째 속성의 id 이다.
- AbstractStore 안의 레코드들은 ids 를 저장하지 않는다. 그들의 파일에서의 오프셋은 그들의 id 를 정의하며
그들의 id 는 그들의 파일에서의 오프셋을 정의한다.
6. 관계 스토어
- org.neo4j.kernel.impl.nioneo.store.RelationshipStore 는 neostore.relationshipstore.db 파일을 만든다.
- 33 바이트의 관계 레코드에서,
* 첫번째 바이트는 사용플레그
* 4 바이트 : 첫번째 노드의 id
* 4 바이트 : 두번째 노드의 id
* 4 바이트 : 이 관계의 타입을 나타내는 레코드의 id
* 4 바이트 * 4 : 첫번째 노드의 이전관계 id / 첫번째 노드의 다음 관계 id /
두번째 노드의 이전관계 id / 두번째 노드의 다음 관계 id
* 4 바이트 : 이 관계의 첫번째 속성의 id
- 관계는 디스크상에서 이중링크드리스트의 형태이다.
7. 관계 타입 스토어
- org.neo4j.kernel.impl.nioneo.store.RelationshipTypeStore 는 neostore.relationshipTypestore.db 파일 생성.
- 관계 타입 이름을 저장하기위한 장소가 필요한데 neostore.relationshipTypestore.db.names 파일이다.
8 . 속성 스토어
- org.neo4j.kernel.impl.nioneo.store.PropertyStore 는 neostore.propertystore.db 파일을 만든다.
- 각각의 속성 레코드들은 in_use 바이트과 속성의 타입을 저장하는 integer 로 시작된다.
Block - oriented storage: Strings and arrays
* 이전 스토리지엔진들은 고정크기의 레코드들을 가지고, 다른 레코드들로의 포인터 및 작은 데이터로 구성되었다.
당신의 소중하고, 다양한크기를 가진 문자열 또는 배열 속성 값들은 다른 방식으로 다루어져야한다.
이것이 DynamicStores 가 만들어진 이유이다. 우리는 이미 PropertyStore 나 RelaltionshipTypeStore 의 부분으로
그것의 사용법을 살펴보았는데, 모두다 .Strings 나 배열같은 자바 기본 요소가 필요하다.
이제 그들의 구조를 살펴보자.
9. 일반 블록 구조
다양한 사이즈의 임의의 데이터는 dynamic store 파일안에 이중 링크드 블럭 구조에 의해 수용되어진다.
여기서 필요로 되어지는것은 in_use byte / 이전 블럭 / 다음 블럭으로의 포인터 / 데이타의 크기 및 데이터이다.
이것의 크기는 파일안에서의 오프셋을 발견하기 위해 가능한 맥시점 블럭사이즈 에 의해 한계가 지어진다. 만약 하나의
데이터가 blockSize 보다 더 큰 공간을 요구하면, 그 만큼의 블럭들이 그것의 체인에 알맞게 추가된다.
그러나 체인의 마지막 블럭은 (만약 하나라면 첫번째) 할당된 양보다 적게 사용가능하다.
각각 블럭헤더의 레이아웃은 in_use byte, 이전 블럭의 int id (만약 -1 이면 첫번째) , 길이 , 다음블럭의 int id, 이
13바이트 이후에는 맥스 blockSize 크기의 데이터가 온다.전체 블럭의 크기는 고로 13 + blockSize 이다.
10. Strings vs Arrays
org.neo4j.kernel.impl.nioneo.store.DynamicArrayStore
org.neo4j.kernel.impl.nioneo.store.DynamicStringStore
11. 개인적 노트

12. 마무리
몇몇 부분 정확한 이해없이 대략 정리한것이기때문에 잘못된것이 있을수 있습니다.
하기 레퍼런스를 보시면 더 정확한 정보를 알수있습니다.
레퍼런스:
http://neo4j.com/docs/stable/tutorials-java-embedded-hello-world.html
http://digitalstain.blogspot.kr/2010/10/neo4j-internals-file-storage.html