Дизайн таблицы DynamoDB для социальной сети

У меня проблема с мышлением в DynamoDB. Моя структура выглядит следующим образом:

  • первичный ключ = "id"

  • sort key = "sort" У меня есть сообщения, пользователи и отношения типа "пользователь A следующий за пользователем B".

Пользователи:

  • id=1234
  • sort = "USER_USER_1234"
  • name = "max" (например)

-

  • id=3245
  • sort = "USER_USER_3245"
  • name = "том"

Сообщение:

  • id=9874

  • sort = "POST_POST_1234 (потому что он создан пользователем с идентификатором 1234)

  • createdAt = 1560371687

Читаемые:

  • id=1234

  • sort = "USER_FOLLOW_3245"

-> Том следует за Максом (но за Максом не за Томом)

Как я могу создать запрос, чтобы получать все сообщения людей, за которыми подписан tom (id = 3245)? Так в моем случае идентификатор сообщения 9874? Мой подход заключался в том, чтобы поместить GSI, где sort является первичным ключом, а id - ключом сортировки (чтобы я мог запрашивать всех людей, за которыми подписан пользователь A), чем получать все сообщения от пользователей (с помощью того же GSI) и сортировать результат после второго индекса, где createdAt - это ключ сортировки. Проблема в том, что для этого нужно много запросов (представьте, что пользователь A подписался на 10000 человек, и все они писали сообщения). Есть ли метод или подход к дизайну, который вы могли бы порекомендовать в этой ситуации? Мой второй подход заключался в том, чтобы проиндексировать всю таблицу приложения для эластичного поиска и выполнить вложенный запрос. Было бы в этом больше смысла? Или вы порекомендуете использовать другой тип базы данных, например AWS neptune?


person m_____0    schedule 12.06.2019    source источник


Ответы (2)


В Amazon Neptune это было бы очень просто:

g.V(3245).E('post')

Вышеупомянутый запрос вернет итератор для всех вершин, связанных меткой Edge «post», начиная с вершины с идентификатором «3245». Вы можете еще больше усилить его, либо проецируя определенные свойства (.property('name')) из этих вершин, либо материализуя вершину целиком (.valueMap()). Это просто синтаксис Gremlin, и вы можете легко сделать то же самое с помощью SPARQL, а Amazon Neptune поддерживает оба из них.

Более серьезный вопрос для вас - оценить все типы запросов, которые вы хотите выполнять с вашими данными, и посмотреть, имеет ли смысл их моделирование в базе данных графов. Если это так, тогда вам лучше использовать Neptune, а не что-то нестандартное, используя смесь других продуктов. Запросы / просмотр данных с высокой связью, навигация по отношениям и т. Д. - вот некоторые из классических вариантов использования модели данных графа.

person The-Big-K    schedule 18.06.2019
comment
Спасибо друг! Следующее отношение - единственное, где это имело бы смысл. Остальное - это всего лишь одна таблица в Dynamodb, я разработал схему, которая не требует сканирования или других дорогостоящих операций. Как вы думаете, можно ли связать упомянутые отношения с эластичными? Это так хорошо работает с Dynamodb и идеально подходит для геоспитальных запросов, например, в моем приложении. - person m_____0; 19.06.2019
comment
Возможно, это возможно, но вы не запрашиваете и не ищете по содержанию сообщения, верно? Если текстовый поиск является важным вариантом использования, эластичность имеет смысл. Если он просто проходит через соединения и есть вероятность, что вы в конечном итоге пройдете через большие графы, запросы с несколькими переходами и т. Д., Я бы порекомендовал граф. Кроме того, как DDB помогает с геопространственными запросами? - person The-Big-K; 20.06.2019
comment
Я использую Elastic только для гео-запросов, извините, если это было недоразумением. И я не ищу содержание сообщения. - person m_____0; 20.06.2019
comment
Итак, вы используете Elastic для GeoSpatial - что имеет смысл. Геопространственные запросы также являются основным вариантом использования графов (например, уже существуют словари SPARQL для моделирования широты и долготы), но я не думаю, что у Нептуна есть что-то из коробки, что могло бы дать вам мгновенные преимущества для гео-запросов. Любопытно - какое преимущество DDB дает вам для модели данных по сравнению с графическим db? - person The-Big-K; 20.06.2019
comment
DynamoDB эффективен по цене и очень быстро работает, если структура схемы таблицы верна. В моем случае это идеально подходит, но есть только одна проблема с типом отношения, как упомянуто выше. Я думаю, что клонирую сообщения, идентификаторы пользователей и следующие отношения в neptune и сделаю запрос оттуда. С помощью лямбда-триггера легко получить согласованные данные. - person m_____0; 22.06.2019
comment
Если вы хотите полностью материализовать свои строки, то да, DDB - хороший выбор. Если вы хотите спроецировать только некоторые поля и фильтровать / перемещаться на основе этого, вы увидите подъем в Нептуне. Удачи в ваших экспериментах и ​​поделитесь результатами. - person The-Big-K; 22.06.2019

Существует практическое занятие по aws, посвященное аналогичной проблеме - «мобильное приложение, включающее социальную сеть»: https://aws.amazon.com/getting-started/hands-on/design-a-database-for-a-mobile-app-with-Dynamodb/4/

Краткое описание:

  1. Пользователи будут загружать фотографии через ваше приложение
  2. пользователи захотят найти друзей и подписаться на них
  3. Подписавшись на друга, пользователь будет получать уведомления о новых фотографиях друга.
  4. пользователь сможет отправлять сообщения своим друзьям
  5. друзья могут просматривать свои фотографии
  6. пользователи могут реагировать на фотографию одним из четырех смайликов - сердечком, смайликом, большим пальцем вверх или парой солнцезащитных очков.
  7. При просмотре фотографии пользователи должны видеть количество реакций каждого типа на фотографию.

Модель имеет следующие сущности: User, Photo, Reaction, Friendship.

User может иметь много Photos, а Photo может иметь много Reactions. Наконец, сущность Friendship представляет отношение «многие ко многим» между пользователями, поскольку пользователь может следовать за несколькими пользователями, а за ним могут следовать несколько других пользователей.

Шаблоны доступа

Исходя из бизнес-требований, были определены следующие шаблоны доступа:

Пользователь

  1. Создать профиль пользователя (Написать)
  2. Обновить профиль пользователя (Написать)
  3. Получить профиль пользователя (Читать)

Фото

  1. Загрузить фото для пользователя (Написать)
  2. Просмотр последних фотографий пользователя (Читать)
  3. Реагировать на фото (Написать)
  4. Посмотреть фото и реакции (Читать)

Дружба

Пользователи могут подписываться на друзей, просматривать обновления о действиях своих друзей и получать рекомендации о других друзьях, за которыми они, возможно, захотят подписаться.

Дружба - это односторонние отношения, как в Твиттере. Один пользователь может выбрать подписку на другого пользователя, и этот пользователь может подписаться на него в ответ. Для нашего приложения мы будем называть пользователей, которые следят за пользователем, «подписчиками», и мы будем называть пользователей, на которых подписан пользователь, «отслеживаемыми».

Основываясь на этой информации, у нас есть следующие шаблоны доступа:

  1. Следить за пользователем (Написать)
  2. Просмотреть подписчиков для пользователя (Читать)
  3. Просмотр для пользователя (чтение)

В сущности «Дружба» у нас есть шаблон доступа, который должен найти всех пользователей, которые следуют за конкретным пользователем, а также шаблон доступа, чтобы найти всех пользователей, за которыми следует данный пользователь.

Дизайн стола

По этой причине мы будем использовать составной первичный ключ со значением PK и SK. Составной первичный ключ даст нам возможность запроса на PK для удовлетворения одного из необходимых нам шаблонов запросов:

Entity               PK                  SK

User          USER#<USERNAME>          #METADATA#<USERNAME>

Photo         USER#<USERNAME>.         PHOTO#<USERNAME>#<TIMESTAMP>

Reaction  REACTION#<USERNAME>#<TYPE>   PHOTO#<USERNAME>#<TIMESTAMP>

Friendship    USER#<USERNAME>          #FRIEND#<FRIEND_USERNAME>

Сущность «Дружба» использует тот же ПК, что и сущность «Пользователь». Это позволит вам получить как метаданные для пользователя, так и всех его подписчиков в одном запросе:

    KeyConditionExpression="PK = :pk AND SK BETWEEN :metadata AND :photos",
    ExpressionAttributeValues={
        ":pk": { "S": "USER#{}".format(username) },
        ":metadata": { "S": "#METADATA#{}".format(username) },
        ":photos": { "S": "PHOTO$" },
    },

Вторичный (инвертированный) индекс полезен для запроса «другой» стороны отношения «многие ко многим». Так обстоит дело с вашей сущностью Дружбы. С помощью структуры первичного ключа вы можете запрашивать всех подписчиков конкретного пользователя с помощью запроса по первичному ключу таблицы. Когда вы добавляете инвертированный индекс, вы сможете найти пользователей, за которыми подписан пользователь («отслеживаемые»), запросив инвертированный индекс:

    KeyConditionExpression="SK = :sk",
    ExpressionAttributeValues={
        ":sk": { "S": "#FRIEND#{}".format(username) }
    },

Расширения

Было бы интересно настроить дизайн для поддержки мега-популярных пользователей (имеющих миллионы подписчиков).

Еще один интересный шаблон доступа, о котором здесь не упоминается, - это пользовательский канал - просмотр всех фотографий, которые недавно опубликовали их друзья. Это можно сделать с другой таблицей, содержащей этот поток данных, который обновляется всякий раз, когда друг что-то публикует (найти его подписчиков, обновить их ленты ...).

person milan    schedule 12.05.2020