ServiceStack.Net Redis: Almacenamiento de Objetos Relacionados vs. Identificadores de Objetos Relacionados


Mi equipo ha decidido trabajar con Redis a través del ServiceStack.net Redis Client como repositorio subyacente para un nuevo sitio web de gran volumen en el que estamos trabajando. No estoy muy seguro de dónde buscar documentación para esta pregunta (ya sea para documentos generales de Redis o específicos ServiceStack.Net documentos o ambos) - ¿existe realmente una fuente definitiva de documentación sobre cómo implementar un Redis a través de ServiceStack.Net eso incluye todo lo que necesita saber sobre los conceptos de Redis y ServiceStack.Net conceptos, o tenemos que integrar la documentación de ambos aspectos por separado para obtener la imagen completa?.

Solo estoy lidiando con cómo almacenar exactamente objetos relacionados en el gráfico de objetos de nuestro modelo. Aquí hay un escenario simple con el que quiero trabajar:

Hay dos objetos en el sistema: User y Feed. En términos RDBMS estos dos objetos tienen una relación uno a muchos, es decir, un User tiene una colección de Feed objetos y un feed solo puede pertenecer a uno User. Feeds will siempre se puede acceder desde Redis a través de su usuario, pero ocasionalmente queremos obtener acceso al usuario a través de una instancia de feed.

Así que la pregunta que tengo es si deberíamos almacenar los objetos relacionados como propiedades o deberíamos almacenar los valores Id de los objetos relacionados? Para ilustrar:

Aproximación 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; }
}

Aproximación 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 );
    }
}

¿Cuál de los enfoques anteriores funcionará mejor? He visto ambos enfoques utilizados en varios ejemplos, pero Tengo la impresión de que algunos de los ejemplos que he visto pueden no ser mejores prácticas.

Unas simples preguntas relacionadas:

  • Si realizo un cambio en un objeto, ¿se reflejará automáticamente en Redis o requerirá un guardado? Estoy asumiendo lo último, pero necesito ser absolutamente claro.
  • Si (puedo) usar el Enfoque A, se reflejará una actualización al objeto de usuario X en todo el gráfico de objetos donde se haga referencia o será necesario guardar los cambios ¿a través del gráfico?
  • ¿Hay algún problema con almacenar un objeto a través de su interfaz (es decir, usar IList<Feed> en lugar de List<Feed>?

Lo siento si estas preguntas son un poco básicas - hasta hace 2 semanas nunca había oído hablar de Redis - y mucho menos ServiceStack - (ni tenía a nadie en mi equipo) así que realmente estamos empezando desde cero aquí...

Author: Zac Seth, 2012-01-18

1 answers

En lugar de re-hash una gran cantidad de otra documentación que está por ahí en la naturaleza, voy a enumerar un par de alrededor para algunos antecedentes sobre el cliente Redis de Redis + ServiceStack:

No hay magia - Redis es un lienzo en blanco

Primero quiero señalar que el uso de Redis como un almacén de datos solo proporciona un lienzo en blanco y no tiene ningún concepto de entidades relacionadas por sí mismo. es decir, solo proporciona acceso a estructuras de datos comp-sci distribuidas. Cómo se almacenan las relaciones depende en última instancia del controlador del cliente (es decir, ServiceStack C# Redis Client) o del desarrollador de la aplicación, mediante el uso de las operaciones de estructura de datos primitivas de Redis. Puesto que todos las principales estructuras de datos están implementadas en Redis, básicamente tiene total libertad sobre cómo desea estructurar y almacenar sus datos.

Piense cómo estructuraría las relaciones en código

Entonces, la mejor manera de pensar en cómo almacenar cosas en Redis, es ignorar completamente cómo se almacenan los datos en una tabla RDBMS y pensar en cómo se almacenan en su código, es decir, utilizando las clases de colección C # integradas en la memoria, que Redis refleja en el comportamiento con sus estructuras de datos del lado del servidor.

A pesar de no tener un concepto de entidades relacionadas, las estructuras de datos integradas de Redis Set y SortedSet proporcionan la forma ideal de almacenar índices. Por ejemplo, la colección de Redis Set solo almacena un máximo de 1 ocurrencia de un elemento. Esto significa que puede agregar elementos/claves/ids de forma segura y no le importa si el elemento ya existe, ya que el resultado final será el mismo si lo hubiera llamado 1 o 100 veces, es decir, es idempotente y, en última instancia, solo 1 elemento permanece almacenado en el conjunto. Por lo tanto, un caso de uso común cuando se almacena un gráfico de objetos (raíz agregada) es almacenar los Id de Entidad Hijo (también conocidas como Claves Foráneas) en un Conjunto cada vez que guarda el modelo.

Visualizando sus datos

Para una buena visualización de cómo se almacenan las Entidades en Redis, recomiendo instalar Redis Admin UI que funciona bien con el cliente C# Redis de ServiceStack, ya que utiliza la convención de nomenclatura de claves a continuación para proporcionar una, agrupar las entidades escritas (a pesar de que todas las claves existan en el mismo espacio de claves global).

Para ver y editar una Entidad, haga clic en el enlace Edit para ver y modificar la representación JSON interna de la entidad seleccionada. Esperamos que pueda tomar mejores decisiones sobre cómo diseñar sus modelos una vez que pueda ver cómo se almacenan.

Cómo se almacenan POCO / Entities

El cliente C # Redis funciona con cualquier POCO que tenga una sola clave primaria-que por defecto se espera que sea Id (aunque esta convención se puede anular con ModelConfig). Esencialmente, POCOs se almacena en Redis como JSON serializado con typeof(Poco).Name y Id utilizados para formar una clave única para esa instancia. Por ejemplo:

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

Los pocos en el cliente C# se serializan convencionalmente usando el serializador rápido Json de ServiceStack donde solo se serializan las propiedades con getters públicos (y los setters públicos para ser serializados de nuevo).

Los valores predeterminados se pueden reemplazar con [DataMember] attrs, pero no se recomiendan ya que uglifica sus POCOs.

Las entidades son blobbed

Por lo tanto, sabiendo que POCOs en Redis son solo blobbed, solo desea mantener los datos raíz no agregados en sus POCOs como propiedades públicas (a menos que quiera almacenar datos redundantes a propósito). Una buena convención es usar métodos para obtener los datos relacionados (ya que no se serializarán), pero también le dice a su aplicación qué métodos hacen que sea remoto llamadas para leer datos.

Entonces, la pregunta sobre si el Feed debe almacenarse con el Usuario es si se trata o no de datos raíz no agregados, es decir, si desea o no acceder a los feeds de usuarios fuera del contexto del usuario. Si no, deje la propiedad List<Feed> Feeds en el tipo User.

Mantener Índices Personalizados

Sin embargo, si desea mantener todos los feeds accesibles de forma independiente, es decir, con redisFeeds.GetById(1), entonces querrá almacenarlo fuera de el usuario y mantener un índice de vinculación de las 2 entidades.

Como has notado, hay muchas maneras de almacenar relaciones entre entidades y cómo lo haces es en gran medida una cuestión de preferencia. Para la entidad hija en una relación padre>hijo siempre querrá almacenar el ParentId con la entidad hija. Para el Padre, puede elegir almacenar una colección de ChildIds con el modelo y luego hacer una sola búsqueda para todas las entidades secundarias para rehidrata el modelo.

Otra forma es mantener el índice fuera del dto padre en su propio Conjunto para cada instancia padre. Algunos buenos ejemplos de esto están en el código fuente C# de la demo Redis StackOverflow donde la relación de Users > Questions y Users > Answers se almacena en:

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

Aunque el redescliente de C# incluye soporte para una convención Padre/Hijo predeterminada a través de su TParent.Entidades de almacenamiento(), TParent.GetRelatedEntities<TChild>() y TParent.DeleteRelatedEntities() API donde se mantiene un índice detrás de la escena que se parece a:

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

Efectivamente, estas son solo algunas de sus opciones posibles, donde hay muchas formas diferentes de lograr el mismo fin y en las que también tiene la libertad de rodar la suya propia.

Las libertades de escritura libre y sin esquema de NoSQL deben ser aceptadas y no debe preocuparse por tratar de seguir una estructura rígida y predefinida con la que podría estar familiarizado al usar un RDBMS.

En conclusión, no hay una verdadera manera correcta para almacenar datos en Redis, por ejemplo, el Cliente de C# Redis hace algunas suposiciones para proporcionar una API de alto nivel alrededor de POCOs y blobs los POCOs en los valores de cadena seguros para binarios de Redis, aunque hay otros clientes que preferirán almacenar las propiedades de una entidad en Hashes de Redis (Diccionarios) en su lugar. Ambos funcionarán.

 65
Author: mythz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:17:37