Neo4j 및 Java를 사용한 빅 데이터 분석, 1 부

관계형 데이터베이스는 수십 년 동안 데이터 관리를 지배 해 왔지만 최근에는 NoSQL 대안에 대한 기반을 잃었습니다. NoSQL 데이터 저장소가 모든 사용 사례에 적합한 것은 아니지만 일반적으로 대용량 데이터 를 처리하는 시스템의 약어 인 빅 데이터에 더 적합 합니다. 빅 데이터에는 네 가지 유형의 데이터 저장소가 사용됩니다.

  • Memcached 및 Redis와 같은 키 / 값 저장소
  • MongoDB, CouchDB 및 DynamoDB와 같은 문서 지향 데이터베이스
  • Cassandra 및 HBase와 같은 열 지향 데이터 저장소
  • Neo4j 및 OrientDB와 같은 그래프 데이터베이스

이 튜토리얼에서는 관련성높은 데이터 와 상호 작용하는 데 사용되는 그래프 데이터베이스 인 Neo4j를 소개 합니다 . 관계형 데이터베이스는 데이터 간의 관계를 관리하는 데 능숙하지만 그래프 데이터베이스는 n 번째 를 관리하는 데 더 좋습니다.학위 관계. 예를 들어, 친구, 친구의 친구 등과 관련된 패턴을 분석하려는 소셜 네트워크를 생각해보십시오. 그래프 데이터베이스를 사용하면 "5 단계 분리를 감안할 때 내 소셜 네트워크에서 아직 보지 못한 5 개의 영화가 인기가 있습니까?"와 같은 질문에 쉽게 대답 할 수 있습니다. 이러한 질문은 추천 소프트웨어에서 일반적이며 그래프 데이터베이스는 문제 해결에 적합합니다. 또한 그래프 데이터베이스는 액세스 제어, 제품 카탈로그, 영화 데이터베이스 또는 네트워크 토폴로지 및 조직도와 같은 계층 적 데이터를 잘 표현합니다. 여러 관계가있는 개체가있는 경우 그래프 데이터베이스가 이러한 개체를 관리하기위한 우아한 개체 지향 패러다임을 제공한다는 것을 금방 알 수 있습니다.

그래프 데이터베이스의 경우

이름에서 알 수 있듯이 그래프 데이터베이스는 데이터 그래프를 잘 표현합니다. 이것은 누군가와 연결할 때마다 관계가 정의되는 소셜 소프트웨어에 특히 유용합니다. 아마도 지난 구직에서 관심이있는 몇 개의 회사를 선택한 다음 소셜 네트워크에서 이들과의 연결을 검색했을 것입니다. 해당 회사 중 하나에서 일하는 사람을 모를 수도 있지만 소셜 네트워크의 누군가는 알 수 있습니다. 이와 같은 문제를 해결하는 것은 1 ~ 2 단계 (친구 또는 친구의 친구)로 쉽게 분리되지만 네트워크를 통해 검색을 확장하기 시작하면 어떻게됩니까?

그들의 책 Neo4j In Action에서 Aleksa Vukotic과 Nicki Watt는 소셜 네트워크 문제를 해결하기 위해 관계형 데이터베이스와 그래프 데이터베이스의 차이점을 탐구합니다. 그래프 데이터베이스가 관계형 데이터베이스의 대안으로 점점 인기를 얻고있는 이유를 보여주기 위해 다음 몇 가지 예에 대한 작업을 그릴 것입니다.

복잡한 관계 모델링 : Neo4j 대 MySQL

컴퓨터 과학 관점에서 소셜 네트워크에서 사용자 간의 관계를 모델링 할 때 그림 1과 같은 그래프를 그릴 수 있습니다.

스티븐 헤인즈

사용자는 IS_FRIEND_OF다른 사용자와 관계가 있고 해당 사용자는 IS_FRIEND_OF다른 사용자와 관계가 있습니다. 그림 2는 관계형 데이터베이스에서이를 표현하는 방법을 보여줍니다.

스티븐 헤인즈

USER표와 일대 다 관계가 USER_FRIEND있는 모델 두 사용자 사이의 "친구"관계를 테이블을. 이제 관계를 모델링 했으므로 데이터를 어떻게 쿼리할까요? Vukotic과 Watt는 5 단계 (친구의 친구의 친구의 친구의 친구)의 깊이까지 나가는 별개의 친구의 수를 세어 쿼리 성능을 측정했습니다. 관계형 데이터베이스에서 쿼리는 다음과 같습니다.

 # Depth 1 select count(distinct uf.*) from user_friend uf where uf.user_1 = ? # Depth 2 select count(distinct uf2.*) from user_friend uf1 inner join user_friend uf2 on uf1.user_1 = uf2.user_2 where uf1.user_1 = ? # Depth 3 select count(distinct uf3.*) from t_user_friend uf1 inner join t_user_friend uf2 on uf1.user_1 = uf2.user_2 inner join t_user_friend uf3 on uf2.user_1 = uf3.user_2 where uf1.user_1 = ? # And so on... 

이러한 쿼리에 대해 흥미로운 점은 한 단계 더 나갈 때마다 USER_FRIEND테이블 자체와 조인해야한다는 것입니다. 표 1은 연구원 Vukotic과 Watt가 각각 약 50 개의 관계 (50,000 개의 관계)를 가진 1,000 명의 사용자를 삽입하고 쿼리를 실행했을 때 찾은 내용을 보여줍니다.

표 1. 다양한 깊이의 관계에 대한 MySQL 쿼리 응답 시간

DepthExecution 시간 (초) 카운트 결과

2 0.028 ~ 900
0.213 ~ 999
4 10.273 ~ 999
5 92.613 ~ 999

MySQL은 최대 3 단계까지 데이터를 결합하는 훌륭한 작업을 수행하지만 그 이후에는 성능이 빠르게 저하됩니다. 그 이유는 USER_FRIEND테이블이 자체적으로 조인 될 때마다 MySQL은 대부분의 데이터가 버려지더라도 테이블의 데카르트 곱을 계산해야하기 때문입니다. 예를 들어이 조인을 다섯 번 수행하면 데카르트 곱은 50,000 ^ 5 행 또는 102.4 * 10 ^ 21 행이됩니다. 1,000 개에만 관심이있는 것은 낭비입니다!

다음으로 Vukotic과 Watt는 Neo4j에 대해 동일한 유형의 쿼리를 실행하려고했습니다. 완전히 다른 결과가 표 2에 나와 있습니다.

표 2. 다양한 깊이의 관계에 대한 Neo4j 응답 시간

DepthExecution 시간 (초) 카운트 결과

2 0.04 ~ 900
0.06 ~ 999
4 0.07 ~ 999
5 0.07 ~ 999

이러한 실행 비교에서 테이크 아웃은 하지 Neo4j는 MySQL을보다 더 낫다는 것을. 오히려 이러한 유형의 관계를 탐색 할 때 Neo4j의 성능은 검색된 레코드 수에 따라 달라지는 반면 MySQL의 성능은 USER_FRIEND테이블 의 레코드 수에 따라 달라집니다 . 따라서 관계 수가 증가하면 MySQL 쿼리에 대한 응답 시간도 마찬가지로 증가하는 반면 Neo4j 쿼리에 대한 응답 시간은 동일하게 유지됩니다. 이는 Neo4j의 응답 시간이 총 관계 수가 아니라 특정 쿼리에 대한 관계 수에 따라 달라지기 때문입니다.

빅 데이터를위한 Neo4j 확장

이 사고 프로젝트를 한 단계 더 확장하여 Vukotic과 Watt는 다음으로 5 천만 명의 관계를 가진 백만 명의 사용자를 생성했습니다. 표 3은 해당 데이터 세트에 대한 결과를 보여줍니다.

표 3. 5 천만 관계에 대한 Neo4j 응답 시간

DepthExecution 시간 (초) 카운트 결과

2 0.01 ~ 2,500
0.168 ~ 110,000
4 1.359 ~ 600,000
5 2.132 ~ 800,000

말할 필요도없이, 나는 Aleksa Vukotic과 Nicki Watt에게 빚을지고 있으며 그들의 작품을 확인하는 것이 좋습니다. 이 섹션의 모든 테스트는 그들의 책 Neo4j in Action 의 첫 번째 장에서 추출했습니다 .

Neo4j 시작하기

You've seen that Neo4j is capable of executing massive amounts of highly related data very quickly, and there's no doubt it's a better fit than MySQL (or any relational database) for certain kinds of problems. If you want to understand more about how Neo4j works, the easiest way is to interact with it through the web console.

Start by downloading Neo4j. For this article, you'll want the Community Edition, which as of this writing is at version 3.2.3.

  • On a Mac, download a DMG file and install it as you would any other application.
  • On Windows, either download an EXE and walk through an installation wizard or download a ZIP file and decompress it on your hard drive.
  • On Linux, download a TAR file and decompress it on your hard drive.
  • Alternatively, use a Docker image on any operating system.

Neo4j를 설치했으면 시작하고 다음 URL로 브라우저 창을 엽니 다.

//127.0.0.1:7474/browser/

기본 사용자 이름 neo4j과 기본 암호를 사용하여 로그인 합니다 neo4j. 그림 3과 유사한 화면이 표시되어야합니다.

스티븐 헤인즈

Neo4j의 노드 및 관계

Neo4j는 노드 및 관계 개념을 중심으로 설계되었습니다.

  • 노드는 이러한 사용자, 동영상, 책 등의 일을 나타냅니다.
  • 노드에는 이름, 제목 또는 게시자와 같은 키 / 값 쌍 집합이 포함 됩니다.
  • 노드의 레이블 은 사용자, 영화 또는 책과 같은 유형의 항목을 정의합니다.
  • 관계 는 노드 간의 연결을 정의하며 특정 유형입니다.

예를 들어 Iron Man 및 Captain America와 같은 Character 노드를 정의 할 수 있습니다. "Avengers"라는 Movie 노드를 정의합니다. 그리고 APPEARS_INIron Man과 Avengers, Captain America와 Avengers 사이 의 관계 를 정의합니다 . 이 모든 것이 그림 4에 나와 있습니다.

스티븐 헤인즈

그림 4는 3 개의 노드 (2 개의 Character 노드와 1 개의 Movie 노드)와 2 개의 관계 (둘 다 유형 APPEARS_IN)를 보여줍니다.

노드 및 관계 모델링 및 쿼리

관계형 데이터베이스가 SQL (Structured Query Language)을 사용하여 데이터와 상호 작용하는 방식과 유사하게 Neo4j는 Cypher Query Language를 사용하여 노드 및 관계와 상호 작용합니다.

Cypher를 사용하여 가족의 간단한 표현을 만들어 보겠습니다. 웹 인터페이스 상단에서 달러 기호를 찾습니다. 이것은 Neo4j에 대해 직접 Cypher 쿼리를 실행할 수있는 필드를 나타냅니다. 해당 필드에 다음 Cypher 쿼리를 입력합니다 (저는 가족을 예로 사용하고 있지만 원하는 경우 자신의 가족을 모델링하도록 세부 정보를 자유롭게 변경할 수 있습니다).

CREATE (person:Person {name: "Steven", age: 45}) RETURN person

결과는 그림 5에 나와 있습니다.

스티븐 헤인즈

그림 5에서 레이블 Person과 Steven이라는 이름의 새 노드를 볼 수 있습니다. 웹 콘솔에서 노드 위로 마우스를 가져 가면 하단에 해당 속성이 표시됩니다. 이 경우 속성은 ID : 19, 이름 : Steven 및 나이 : 45입니다. 이제 Cypher 쿼리를 분석해 보겠습니다.

  • CREATE: The CREATE keyword is used to create nodes and relationships. In this case, we pass it a single argument, which is a Person enclosed in parentheses, so it is meant to create a single node.
  • (person: Person {...}): The lower case "person" is a variable name through which we can access the person being created, while the capital "Person" is the label. Note that a colon separates the variable name from the label.
  • {name: "Steven, age: 45}: These are the key/value properties that we're defining for the node we're creating. Neo4j does not require you to define a schema before creating nodes and each node can have a unique set of elements. (Most of the time you define nodes with the same label to have the same properties, but it is not required.)
  • RETURN person: After the node is created, we ask Neo4j to return it back to us. This is why we saw the node appear in the user interface.

The CREATE command (which is case insensitive) is used to create nodes and can be read as follows: create a new node with the Person label that contains name and age properties; assign it to the person variable and return it back to the caller.

Querying with Cypher Query Language

Next we want to try some querying with Cypher. First, we'll need to create a few more people, so that we can define relationships between them.

 CREATE (person:Person {name: "Michael", age: 16}) RETURN person CREATE (person:Person {name: "Rebecca", age: 7}) RETURN person CREATE (person:Person {name: "Linda"}) RETURN person 

Once you've created your four people, you can either click on the Person button under the Node Labels (visible if you click on the database icon in the upper left corner of the web page) or execute the following Cypher query:

MATCH (person: Person) RETURN person

Cypher uses the MATCH keyword to find things in Neo4j. In this example, we are asking Cypher to match all nodes that have a label of Person, assign those nodes to the person variable, and return the value that is associated with that variable. As a result you should see the four nodes that you've created. If you hover over each node in your web console, you will see each person's properties. (You might note that I excluded my wife's age from her node, illustrating that properties do not need to be consistent across nodes, even of the same label. I am also not foolish enough to publish my wife's age.)

We can extends this MATCH example a little further by adding conditions to the nodes we want returned. For example, if we wanted just the "Steven" node, we could retrieve it by matching on the name property:

MATCH (person: Person {name: "Steven"}) RETURN person

Or, if we wanted to return all of the children we could request all people having an age under 18:

MATCH (person: Person) WHERE person.age < 18 RETURN person

In this example we added the WHERE clause to the query to narrow our results. WHERE works very similarly to its SQL equivalent: MATCH (person: Person) finds all nodes with the Person label, and then the WHERE clause filters values out of the result set.

Modeling direction in relationships

We have four nodes, so let's create some relationships. First of all, let's create the IS_MARRIED_TO relationship between Steven and Linda:

MATCH (steven:Person {name: "Steven"}), (linda:Person {name: "Linda"}) CREATE (steven)-[:IS_MARRIED_TO]->(linda) return steven, linda

이 예에서는 Steven과 Linda라는 레이블이 붙은 두 Person 노드를 일치시키고 IS_MARRIED_TOSteven에서 Linda까지 유형의 관계를 만듭니다 . 관계를 만드는 형식은 다음과 같습니다.

(node1)-[relationshipVariable:RELATIONSHIP_TYPE->(node2)