ServiceStack.Net Redis: сохранение связанных объектов и связанных с ними идентификаторов объектов
Моя команда решила работать с Redis через ServiceStack.net Redis Client как базовый repository для нового крупномасштабного веб-сайта, над которым мы работаем. Я не совсем уверен, где искать документацию по этому вопросу (либо для общих документов Redis, либо для специальных документов ServiceStack.Net, либо для обоих) – существует ли на самом деле окончательный источник документации о том, как реализовать Redis через ServiceStack.Net, который включает все, что вам нужно знать о концепциях Redis и концепциях ServiceStack.Net, или нам нужно отдельно интегрировать документацию из обоих аспектов, чтобы получить полную картину ?.
Я просто разбираюсь в том, как точно хранить связанные объекты в графе объектов нашей модели. Вот простой сценарий, с которым я хочу работать:
В системе есть два объекта: « User
и « Feed
. В RDBMS эти два объекта имеют отношение «один ко многим», то есть User
имеет коллекцию объектов « Feed
а фид может принадлежать только одному User
. Каналы всегда будут доступны из Redis через их пользователя, но иногда мы хотим получить доступ к пользователю через экземпляр фида.
- Рекомендуемая структура API ServiceStack
- Поддерживает ли ServiceStack двоичные ответы?
- ServiceStack и возврат streamа
- Как продлить проверку подлинности ServiceStack
- ServiceStack vs ASP.Net Web API
Таким образом, у меня вопрос заключается в том, следует ли хранить связанные объекты как свойства или хранить значения Id
связанных объектов? Проиллюстрировать:
Подход A :
public class User { public User() { Feeds = new List(); } public int Id { get; set; } public List 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(); } public long Id { get; set; } public List FeedIds { get; set; } public List 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
а неList
?
Извините, если эти вопросы немного базовые – до 2 недель назад я даже не слышал о Redis, не говоря уже о ServiceStack (и не имел никого в моей команде), поэтому мы начинаем с нуля здесь …
Вместо повторного hashа есть много другой документации, которая есть в дикой природе, я перечислил пару для некоторой дополнительной информации о Redis Client Redis + ServiceStack:
- Что нужно учитывать при разработке приложения NoSQL Redis
- Проектирование базы данных NoSQL с использованием Redis
- Общий обзор Redis и .NET
- Сквозное управление версиями и миграция данных с помощью C # Redis Client
Нет волшебства – Редис – пустой canvas
Во-первых, я хочу отметить, что использование Redis в качестве хранилища данных просто предоставляет пустой canvas и не имеет понятия о связанных с ним объектах. т.е. он просто обеспечивает доступ к распределенным структурам данных comp-sci. Как отношения сохраняются, в конечном счете, до клиентского драйвера (например, ServiceStack C # Redis Client) или разработчика приложения, используя операции примитивной обработки данных Redis. Поскольку все основные структуры данных реализованы в Redis, у вас есть полная свобода в отношении того, как вы хотите структурировать и хранить свои данные.
Подумайте, как бы вы структурировали отношения в коде
Поэтому лучший способ подумать о том, как хранить вещи в Redis, – это полностью игнорировать то, как данные хранятся в таблице RDBMS, и думать о том, как они хранятся в вашем коде, то есть использовать встроенные classы коллекции C # в памяти – который Redis отражает поведение с их серверными структурами данных.
Несмотря на отсутствие концепции связанных объектов, встроенные в Redis встроенные структуры данных Set and SortedSet обеспечивают идеальный способ хранения индексов. Например, коллекция Set Redis хранит максимум 1 вхождения элемента. Это означает, что вы можете безопасно добавлять элементы / ключи / идентификаторы к нему и не заботятся о том, существует ли этот элемент уже в качестве конечного результата, если бы вы его назвали 1 или 100 раз, то есть он идемпотент, и в конечном итоге только один элемент остается сохраненным в набор. Таким образом, общий пример использования – это хранение графа объектов (совокупный корень) для хранения идентификаторов дочерних объектов (ака внешних ключей) в наборе каждый раз при сохранении модели.
Визуализация данных
Для хорошей визуализации того, как Entities хранятся в Redis, я рекомендую установить пользовательский интерфейс Redis Admin, который хорошо работает с C # Redis Client ServiceStack, поскольку он использует соглашение о присвоении имен ключа ниже, чтобы обеспечить хорошее иерархическое представление, объединив ваши типизированные сущности (несмотря на все ключи существующих в одном и том же глобальном пространстве ключей).
Чтобы просмотреть и отредактировать Entity, нажмите ссылку « Изменить», чтобы просмотреть и изменить внутреннее представление JSON выбранного объекта. Надеемся, вы сможете принять правильные решения о том, как создавать свои модели, как только вы сможете увидеть, как они хранятся.
Как хранятся POCO / Entities
Клиент C # Redis работает с любыми POCO, которые имеют один первичный ключ, который по умолчанию должен быть Id
(хотя это соглашение переопределяется с ModelConfig ). По существу, POCOs хранится в Redis как сериализованный JSON с именем typeof(Poco).Name
и Id
используемым для формирования уникального ключа для этого экземпляра. Например:
urn:Poco:{Id} => '{"Id":1,"Foo":"Bar"}'
POCOs в C # Client традиционно сериализуются с помощью быстрого Json Serializer ServiceStack, где только свойства с общедоступными геттерами сериализуются (и публичные сеттеры, чтобы получить сериализацию обратно).
Значения по умолчанию переопределяемы с помощью [DataMember]
но не рекомендуются, поскольку он угадывает ваши POCOs.
Объекты размыты
Поэтому, зная, что POCOs в Redis просто blobbed, вы хотите сохранить неагрегатные корневые данные в своих POCO как общедоступные свойства (если только вы не намерены хранить избыточные данные). Хорошим соглашением является использование методов для получения связанных данных (поскольку он не будет сериализован), но также сообщает вашему приложению, какие методы делают удаленные вызовы для чтения данных.
Таким образом, вопрос о том, должен ли Feed быть сохранен с Пользователем, является ли он неагрегантными корневыми данными, то есть, хотите ли вы получать доступ к фидам пользователей вне контекста пользователя? Если нет, то оставьте свойство List
в типе User
.
Ведение пользовательских индексов
Если вы хотите, чтобы все каналы были доступны независимо друг от друга, то есть с помощью redisFeeds.GetById(1)
вы захотите сохранить его за пределами пользователя и сохранить индекс, связывающий два объекта.
Как вы заметили, существует множество способов хранения отношений между сущностями и того, как вы это делаете, во многом зависит от предпочтений. Для дочернего объекта в родительских> дочерних отношениях вы всегда хотите сохранить ParentId с дочерним объектом. Для родителя вы можете либо выбрать коллекцию ChildIds с моделью, а затем сделать одну выборку для всех дочерних объектов, чтобы повторно увлажнить модель.
Другой способ – поддерживать индекс за пределами родительского dto в своем собственном наборе для каждого родительского экземпляра. Вот некоторые из хороших примеров этого в исходном коде C # для демонстрации Redis StackOverflow, в которой хранится взаимосвязь Users > Questions
и Users > Answers
:
idx:user>q:{UserId} => [{QuestionId1},{QuestionId2},etc] idx:user>a:{UserId} => [{AnswerId1},{AnswerId2},etc]
Хотя C # RedisClient включает поддержку стандартного соглашения о родителях / дочерних элементах через свои TParent.StoreRelatedEntities () , TParent.GetRelatedEntities
и TParent.DeleteRelatedEntities()
API, где индекс сохраняется за сценой, которая выглядит так:
ref:Question/Answer:{QuestionId} => [{answerIds},..]
Эффективно это лишь некоторые из ваших возможных вариантов, где есть много разных способов достижения той же цели и в которой у вас также есть своя собственная свобода.
Схемы NoSQL, свободные от свободной печати, должны быть охвачены, и вы не должны беспокоиться о том, чтобы попытаться следовать жесткой, заранее определенной структуре, с которой вы можете ознакомиться при использовании РСУБД.
В заключение, нет реального правильного способа хранения данных в Redis, например, C # Redis Client делает некоторые предположения, чтобы обеспечить API высокого уровня вокруг POCOs и он блокирует POCO в двоично-безопасных строковых значениях Redis – хотя есть и другие клиенты предпочитают вместо этого сохранять свойства объектов в Redis Hashes (словари). Оба будут работать.