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:
- о чем думать при разработке приложения NoSQL Redis
- проектирование базы данных NoSQL с помощью Redis
- общий обзор Redis и .NET
- Schemaless управление версиями и данными Миграции с клиентом C# 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 (словари) вместо этого. Оба будут работать.