2014-04-15 1 views
4

Google Apps Engine은 Google Datastore를 유일한 NoSQL 데이터베이스로 제공합니다 (BigTable을 기반으로 생각합니다).Java - Google App Engine - Google Datastore에서 그래프 구조 모델링

저는 사회적인 데이터 구조를 가지고 있으며 그래프 데이터베이스에서와 같이 모델링하고 싶습니다. 내 응용 프로그램은 이기종 객체 (사용자, 파일, ...) 및 이들 사이의 관계 (예 : user1 OWNS file2, user2 FOLLOWS user3 등)를 저장해야합니다.

나는이 일반적인 상황을 모델링 할 수있는 좋은 방법을 찾고 있어요, 내가 솔루션의 두 가족으로 생각 :

  1. 목록 기반 솔루션 : 모든 객체는 다른 관련 객체와의 목록이 포함되어 있습니다 목록에있는 객체의 존재 자체는 관계입니다 (Google에서 JDO 부분에 말한대로 https://developers.google.com/appengine/docs/java/datastore/jdo/relationships).

  2. 그래프 기반 솔루션 : 노드와 관계는 모두 개체입니다. 객체는 관계와는 독립적으로 존재하지만 각 관계에는 두 개 (또는 그 이상) 연결된 객체에 대한 참조가 포함됩니다.

이 두 가지 접근 방법의 강점과 약점은 무엇입니까? 접근 방식에 대해서 1

:이 사람이 생각할 수있는 간단한 방법이며, 그것은 또한 공식 문서에 제시되어 있지만 :

  • 각 감독 관계가 오브젝트 기록이 성장합니다 제한 사항이에있다 예를 들어 객체 차원 제한에 의해 주어진 가능한 관계의 수?
  • JDO 기능이 있습니까? 아니면 데이터 저장소 구조로 인해 자연스럽게 구현 될 수 있습니까?
  • 관계 검색 시간이 목록과 함께 증가합니다.이 솔루션이 많은 관계에 적합합니까?

접근법 2 : 각 관계는 더 높은 수준의 특성을 가질 수 있습니다 (이는 개체이며 속성을 가질 수 있습니다). 그리고 메모리 크기는 Google 문제가 아니라고 생각합니다.

  • 각 관계에는 자체 레코드가 필요하므로 관계의 총 수가 증가하면 각 관련 커플의 검색 시간이 늘어납니다. 이것은 많은 양의 관계 (수백만, 수십억)에 적합한가? 나는. Google은 잘 구조화되어있는 레코드를 검색하는 좋은 방법이 있습니까? 아니면 곧 User4라는 User1이라는 친구를 검색하고 싶다면 몇 초 기다려야 할 상황에 처할 것입니까?
  • 다른 측면에서는 새로운 관계가 추가 될 때 각 개체의 차원이 증가하지 않습니다.

두 가지 접근법에서 다른 중요한 점을 찾아서 최고의 모델을 선택하도록 도와 주시겠습니까?

답변

5

먼저 Datastore의 검색 시간은 검색하는 엔티티 수에만 저장하는 엔티티 수에 따라 달라지지 않습니다. 따라서 10 억 개 중 하나의 관계 개체를 찾아야하는 경우 하나의 개체 만있는 것처럼 동일한 시간이 걸립니다.

둘째, 목록 접근 방식에는 "폭발 색인"이라는 심각한 제한이 있습니다.검색 가능한 목록을 포함하는 속성을 색인화해야합니다. 이 속성 이외의 다른 참조를 사용하는 쿼리를 사용하는 경우이 문제와 관련이 있습니다. Google의 의미를 이해해야합니다.

셋째, 목록 접근 방식은 훨씬 더 비쌉니다. 새로운 관계를 추가 할 때마다 상당한 비용으로 전체 엔터티를 다시 작성합니다. 키 전용 쿼리를 사용할 수 없으면 읽기 비용도 높아집니다. 객체 접근 방식을 사용하면 키 전용 쿼리를 사용하여 관계를 찾을 수 있으며 이러한 쿼리는 이제 무료입니다.

UPDATE :

의 관계가 지시하는 경우 사용자 단체의 관계 기관의 아이들을,뿐만 아니라 관계 엔티티의 ID와 같은 개체 ID를 사용하여 고려할 수 있습니다. 그런 다음 Relationship 엔티티에는 전혀 특성이 없으므로 가장 비용 효율적인 솔루션입니다. 키 전용 ancestor queries을 사용하여 사용자가 소유 한 모든 객체를 검색 할 수 있습니다.

+2

그리고해야 할 때까지 JDO를 사용하지 마십시오. Objectify 또는 저수준 API를 대신 사용하십시오. –

+0

답장을 보내 주셔서 대단히 감사합니다! 나에게 이해 시켜라. 만약 내가 10 억 가지의 "A"종류의 물건을 가지고 있다면 A.first와 A.second 두 종류의 파일을 가지고 A.First = Y와 같은 종류의 X 레코드를 찾고 싶다. 질의 시간은 ' t는 db에 저장된 객체의 수에 따라 다릅니다. 그래서 나는 1000 개의 레코드를 가지고 동일한 퍼포먼스를 얻었습니까? 두 번째 접근법을 제안 하시겠습니까? 이 접근 방식에는 폭발적인 인덱스와 같은 몇 가지 중요한 측면이 있습니까? – LJSilver

+0

아니면 다른 방법을 제안 하시겠습니까? – LJSilver

-1

질문이 너무 복잡하지만 최선의 해결책을 설명하려고합니다 (파이썬에서는 대답하지만 자바에서는 동일하게 대답 할 것입니다).

class User(db.User): 
    followers = db.StringListProperty() 

단순 추가 팔로어.

user = User.get(key) 
user.followers.append(str(followerKey)) 

이 빨리 다음에 쿼리 및 추종자

User.all().filter('followers', followerKey) # -> followed 

당신이 내가있는이 빠르지 만 더 복잡하고 비용이 많이 드는 할 수 있도록 I/O는 비용이 많이 드는 기록 O /이 쿼리를 허용 :

class User(db.User): 
    followers = db.StringListProperty() 
    follows = db.StringListProperty() 

사용자 삭제가 필요하므로 변경 사항이 복잡하기 때문에 다음과 같이 2 번 작성해야합니다.

관계를 저장할 수도 있지만 추종자와 함께 두 번째 예제보다 복잡하기 때문에 상황이 악화됩니다 ... - 엔터티가 1Mb를 가질 수 있다는 것을 명심하십시오. 제한이 없지만 될 수 있습니다.

+1

두 개 이상의 쓰기가 필요합니다. 그는 엔티티 당 쓰기와 목록의 각 요소 당 쓰기가 필요합니다. App Engine의 쓰기 비용에 대해 읽어보십시오. –

+0

안녕하세요. 답장을 보내 주셔서 감사합니다. 나는 파이썬을 모른다. 그래서 나는 내가 이해하는지 묻는다. 당신은 기본적으로 접근법 1을 구현했습니다. 각 객체는 그 관계가 자체의 목록 속성으로 구현 된 것을 보았습니다. 맞습니까? 하지만 Andrei Volgin이 강조한 문제는 무엇입니까? 그리고 "상점 관계"라는 것이 정확히 무엇을 의미합니까? 더 복잡한 시나리오이므로 더 나쁜 시나리오입니까? – LJSilver

+1

GAE Datastore는 관계형 데이터베이스가 아니므로 관계형 데이터베이스와 마찬가지로 관계를 악용하려고 시도합니다. GAE는 확장 성과 유연성이 뛰어난 그래프로서 그대로 사용합니다. 폭발로 인한 문제는리스트 속성에서 많은 조건을 만들 때입니다. - 필드 또는 여러 필드의 단일 샷'='은 문제가되지 않습니다. 폭발은 여러 필드의 복잡한 색인과 관련이 있습니다. 이 인덱스는 사용자 지정 정렬이나'= '이외의 연산자를 사용하는 경우 필요합니다. 그들이 설명하는 튜브의 비디오를보십시오. 모든 조합은 복잡한 인덱스에 저장되어야하기 때문에. – Chameleon

1

저는 AppEngine 응용 프로그램을 가지고 있으며 두 가지 접근 방법을 사용합니다. 어떤 것이 더 좋은가는 두 가지에 달려 있습니다. 얼마나 많은 관계가있을 수 있는지, 얼마나 자주 관계가 바뀌는 지에 대한 실질적인 한계입니다.

참고 1 : 내 답변은 Objectify 사용 경험과 캐싱 사용량을 기반으로합니다. 마일리지는 다른 접근 방식에 따라 달라질 수 있습니다.

참고 2 : 올바른 데이터 저장소 용어 '이름'대신 여기에 'id'라는 용어를 사용했습니다. 이름은 혼란 스러웠을 것이고 id는 객체화 된 용어와 더 잘 어울립니다.

참석 한 학교와 연결된 사용자를 고려하고 그 반대의 경우도 고려하십시오. 이 경우 두 가지를 모두 수행합니다. '목록'방법의 변형과 함께 사용자를 학교에 연결하십시오. 사용자가 참석 한 학교 ID 목록을 다른 유형/종류이지만 사용자와 동일한 ID를 가진 UserSchoolLinks 항목으로 저장합니다. 예를 들어, 사용자의 id = '6h30n'은 ID가 '6h30n'인 UserSchoolLinks 객체를 저장합니다. 사용자의 학교 목록을 가져와야 할 때마다 키 조회로이 단일 항목을로드하십시오.

그러나 학교에 다니는 사용자의 경우 반대로하지 마십시오. 그 관계에 대해 링크 엔티티를 삽입하십시오. 링크 엔티티의 ID에는 학교의 ID와 사용자의 ID를 조합하여 사용하십시오. 두 ID를 별도의 속성으로 엔티티에 저장하십시오.예를 들어 학교 'g3g0a3'에 다니는 사용자 '6h30n'의 SchoolUserLink는 'g3g0a3 ~ 6h30n'이라는 ID를 가져 오며 school = g3g0a3 및 user = 6h30n 필드를 포함합니다. 학교 속성에 대한 쿼리를 사용하여 학교에 대한 모든 SchoolUserLinks를 가져옵니다.

  1. 사용자가 자주 학교를 볼 수 있지만 거의 변경할 것 :

    는 그 이유는. 이 접근 방식을 사용하면 사용자의 학교는 캐싱되며 프로필을 공격 할 때마다 가져올 필요가 없습니다.

  2. 키 조회를 통해 사용자의 학교가 나올 예정이므로 검색어를 사용하지 않습니다. 따라서 사용자 학교의 최종 일관성을 처리 할 필요가 없습니다.

  3. 학교에는 여러 사용자가 참석할 수 있습니다. 이 관계를 링크 엔티티로 저장하면 거대한 단일 객체를 만들지 않아도됩니다.

  4. 학교에 다니는 사용자가 많이 바뀔 것입니다. 이렇게하면 우리는 하나의 큰 개체를 자주 작성할 필요가 없습니다.

  5. 사용자 엔티티의 ID를 UserSchoolLinks 엔티티의 ID로 사용하면 사용자의 ID 만 알 수있는 링크를 가져올 수 있습니다.

  6. 학교 ID와 사용자 ID를 SchoolUser 링크의 ID로 결합합니다. 주요 조회를 통해 사용자와 학교가 연결되어 있는지 확인할 수 있습니다. 다시 한 번, 결과 일관성에 대해 걱정할 필요가 없습니다.

  7. 사용자 ID를 SchoolUserLink의 속성으로 포함시킴으로써 사용자의 ID를 얻기 위해 SchoolUserLink 객체를 구문 분석 할 필요가 없습니다. 또한이 필드를 사용하여 양방향 간의 일관성을 확인하고 사람들이 수백 개의 학교에 다니는 경우에 대비하여 폴백을 사용할 수 있습니다.

하향 : 1.이 접근법은 DRY 원칙을 위반합니다. 최소한의 악마처럼 보입니다. 2. 우리는 여전히 학교에 다녔던 사용자를 얻기 위해 쿼리를 사용해야합니다. 그것은 결국 일관성을 유지한다는 것을 의미합니다.

UserSchoolLinks 엔터티를 업데이트하고 트랜잭션에서 SchoolUserLink 엔터티를 추가/제거하십시오.