Подтвердить что ты не робот

ServiceStack.Net Redis: сохранение связанных объектов и связанных с ними идентификаторов объектов

Моя команда решила работать с Redis через ServiceStack.net Redis Client как базовый репозиторий для нового крупномасштабного веб-сайта, над которым мы работаем. Я не совсем уверен, где искать документацию по этому вопросу (либо для общих документов Redis, либо для специальных документов ServiceStack.Net, либо для обоих) - существует ли на самом деле окончательный источник документации о том, как реализовать Redis через ServiceStack.Net, который включает все, что вам нужно знать о концепциях Redis и концепциях ServiceStack.Net, или нам нужно отдельно интегрировать документацию из обоих аспектов, чтобы получить полную картину?.

Я просто разбираюсь в том, как точно хранить связанные объекты в нашем графике объектов модели. Вот простой сценарий, с которым я хочу работать:

В системе есть два объекта: User и Feed. В терминах СУРБД эти два объекта имеют отношение "один ко многим", то есть User имеет коллекцию объектов Feed, а фид может принадлежать только одному User. Каналы всегда будут доступны из Redis через их пользователя, но иногда мы хотим получить доступ к пользователю через экземпляр фида.

Итак, у меня вопрос: нужно ли хранить связанные объекты как свойства или хранить значения Id связанных объектов? Чтобы проиллюстрировать:

Подход A:

public class User
{
    public User()
    {
        Feeds = new List<Feed>();
    }

    public int Id { get; set; }

    public List<Feed> Feeds { get; set; }

    // Other properties
}

public class Feed
{
    public long Id { get; set; }

    public User User { get; set; }
}

Подход B:

public class User
{
    public User()
    {
        FeedIds = new List<long>();
    }

    public long Id { get; set; }

    public List<long> FeedIds { get; set; } 

    public List<Feed> GetFeeds()
    {
        return repository.GetFeeds( FeedIds );
    }
}

public class Feed
{
    public long Id { get; set; }

    public long UserId { get; set; }

    public User GetUser()
    {
        return repository.GetUser( UserId );
    }
}

Какой из вышеперечисленных подходов будет работать лучше всего? Я видел оба подхода, используемые в различных примерах, но у меня создается впечатление, что некоторые из примеров, которые я видел, могут быть не лучшими практиками.

Несколько простых связанных вопросов:

  • Если я сделаю изменение объекта, оно будет автоматически отражено в Redis или потребуется ли сохранить его? Я предполагаю последнее, но должен быть абсолютно ясным.
  • Если я (могу) использовать подход А, будет ли обновление объекта User X отражено на всем объектном графе, где бы оно ни ссылалось, или будет необходимо сохранить изменения на графике?
  • Есть ли проблема с сохранением объекта через его интерфейс (т.е. использовать IList<Feed> в отличие от List<Feed>?

Извините, если эти вопросы немного базовые - до 2 недель назад я даже не слышал о Redis, не говоря уже о ServiceStack (и не имел никого в моей команде), поэтому мы действительно начинаем с нуля здесь...

4b9b3361

Ответ 1

Вместо повторного хэша есть много другой документации, которая существует в дикой природе, я перечислил пару для некоторой дополнительной информации о Redis + ServiceStack Redis Client:

Нет волшебства - Redis - пустой холст

Сначала я хочу указать, что использование Redis в качестве хранилища данных просто предоставляет пустой холст и не имеет понятия самозанятых объектов. то есть он просто обеспечивает доступ к распределенным структурам данных comp-sci. Как отношения хранятся, в конечном счете, до клиентского драйвера (например, ServiceStack С# Redis Client) или разработчика приложения, используя операции примитивной обработки данных Redis. Поскольку все основные структуры данных реализованы в Redis, вы в основном имеете полную свободу в отношении того, как вы хотите структурировать и хранить ваши данные.

Подумайте, как бы вы структурировали отношения в коде

Итак, лучший способ подумать о том, как хранить вещи в Redis, - это полностью игнорировать то, как данные хранятся в таблице RDBMS, и думать о том, как они хранятся в вашем коде, т.е. использовать встроенные классы коллекции С# в памяти - что Redis отражает поведение со своими серверными структурами данных.

Несмотря на отсутствие понятия связанных объектов, встроенные структуры данных Установить и SortedSet обеспечивают идеальный способ хранения индексов. Например. В коллекции Redis Set хранится максимум 1 вхождение элемента. Это означает, что вы можете безопасно добавлять элементы/ключи/идентификаторы к нему и не заботятся о том, существует ли элемент уже в качестве конечного результата, если бы вы его назвали 1 или 100 раз, то есть он идемпотент, и, в конечном счете, только один элемент остается в памяти набор. Таким образом, общий случай использования заключается в том, что хранение графа объектов (агрегированного корня) заключается в том, чтобы хранить идентификаторы дочерних объектов (иначе называемые внешние ключи) в наборе каждый раз, когда вы сохраняете модель.

Визуализация ваших данных

Для хорошей визуализации того, как объекты хранятся в Redis, я рекомендую установить Redis Admin UI, который хорошо работает с ServiceStack С# Redis Client как он использует соглашение о присвоении имен ключа ниже, чтобы обеспечить хорошее иерархическое представление, группируя свои типизированные сущности вместе (несмотря на все ключи, существующие в одном и том же глобальном пространстве ключей).

Чтобы просмотреть и отредактировать Entity, нажмите ссылку Изменить, чтобы просмотреть и изменить выбранное внутреннее представление JSON. Надеемся, вы сможете принять правильные решения о том, как создавать свои модели, как только вы сможете увидеть, как они хранятся.

Как хранится POCO/Entities

Клиент С# Redis работает с любыми POCOs, у которых есть единственный первичный ключ, который по умолчанию должен быть Id (хотя это соглашение переопределяется с ModelConfig), По существу, POCOs хранится в Redis как сериализованный JSON с использованием typeof(Poco).Name и Id, используемых для формирования уникального ключа для этого экземпляра. Например:

urn:Poco:{Id} => '{"Id":1,"Foo":"Bar"}'

POCOs в С# Client обычно сериализуются с использованием ServiceStack fast Json Serializer, где сериализуются только свойства с публичными геттерами (и публичные сеттеры для получения де-сериализованная спина).

Значение по умолчанию переопределяется с помощью [DataMember] attrs, но не рекомендуется, поскольку оно угадывает ваши POCOs.

Объекты blobbed

Итак, зная, что POCOs в Redis просто blobbed, вы хотите сохранить неагрегатные корневые данные в своих POCO как общедоступные свойства (если вы не намерены хранить избыточные данные). Хорошим соглашением является использование методов для получения связанных данных (поскольку он не будет сериализован), но также сообщает вашему приложению, какие методы делают удаленные вызовы для чтения данных.

Итак, вопрос о том, следует ли хранить Feed с помощью Пользователь, является ли он неагрегировать данные root, то есть, хотите ли вы получить доступ к пользователи питаются вне контекста пользователя? Если нет, то оставьте свойство List<Feed> Feeds в типе User.

Поддержание пользовательских индексов

Если вы хотите, чтобы все каналы были доступны независимо, т.е. с помощью redisFeeds.GetById(1), тогда вы захотите сохранить его за пределами пользователя и сохранить индекс, связывающий 2 объекта.

Как вы заметили, существует множество способов хранения отношений между сущностями и того, как вы это делаете, во многом зависит от предпочтений. Для дочернего объекта в родительском > дочернем отношениях вы всегда хотите сохранить ParentId с дочерним объектом. Для родителя вы можете либо выбрать коллекцию ChildIds с моделью, а затем сделать одну выборку для всех дочерних объектов, чтобы повторно увлажнить модель.

Другой способ - поддерживать индекс за пределами родительского dto в своем собственном Установить для каждого родительского экземпляра. Некоторые хорошие примеры этого в С# Исходный код Redis Демо-версия StackOverflow, где отношения Users > Questions и Users > Answers хранятся в:

idx:user>q:{UserId} => [{QuestionId1},{QuestionId2},etc]
idx:user>a:{UserId} => [{AnswerId1},{AnswerId2},etc]

Хотя С# RedisClient включает поддержку условного соглашения родителя/ребенка по умолчанию через TParent.StoreRelatedEntities(), TParent.GetRelatedEntities<TChild>() и TParent.DeleteRelatedEntities() API, где индекс сохраняется за сценой, которая выглядит следующим образом:

ref:Question/Answer:{QuestionId} => [{answerIds},..]

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

Отсутствие схем NoSQL, свободная печать, должны быть приняты, и вам не следует беспокоиться о том, чтобы попытаться следовать жесткой, заранее определенной структуре, с которой вы можете ознакомиться при использовании РСУБД.

В заключение нет реального правильного пути для хранения данных в Redis, например. Клиент С# Redis делает некоторые предположения для предоставления API высокого уровня вокруг POCOs и он блокирует POCOs в двоично-безопасных строковых значениях Redis - хотя есть и другие клиенты, предпочитающие хранить свойства объектов в Redis Hashes (словари) вместо, Оба будут работать.