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

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

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

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

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

Подход:

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 или потребует сохранения? Я предполагаю последнее, но должен быть абсолютно ясен.
  • если я (могу) использовать подход A, будет ли обновление объекта пользователя X отражаться повсюду весь граф объектов, на который ссылаются, или необходимо сохранить изменения на графике?
  • есть ли проблема с хранением объекта через его интерфейс (т. е. использовать IList<Feed> в противоположность List<Feed>?

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

1 ответов


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

нет магии-редис-это чистый холст

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

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

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

несмотря на отсутствие концепции связанных сущностей, встроенный Redis Set и объект sortedset структуры данных обеспечивают идеальный способ хранения индексов. Е. Г. Рэдис это Set коллекция хранит не более 1 вхождения элемента. Это означает, что вы можете безопасно добавлять к нему элементы / ключи / идентификаторы и не заботиться о том, существует ли элемент уже в качестве конечного результата то же самое вы назвали его 1 или 100 раз - т. е. он идемпотентен, и в конечном итоге в наборе остается только 1 элемент. Таким образом, общий случай использования при хранении графа объекта (aggregate root) заключается в хранении идентификаторов дочерних сущностей (он же внешние ключи) в наборе каждый раз при сохранении модели.

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

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

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

как хранятся Poco / сущности

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

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

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

по умолчанию переопределяемый с [DataMember] у attrs, но не рекомендуется, поскольку это uglifies свой покос.

объекты blobbed

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

поэтому вопрос о том, является ли Feed должен храниться с пользователей является ли это неагрегатными корневыми данными, т. е. Хотите ли вы получить доступ к каналам пользователей вне контекста пользователя? Если нет, то оставьте List<Feed> Feeds собственности на User тип.

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

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

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

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

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

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

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

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

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

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