Compartir a través de


Este artículo proviene de un motor de traducción automática.

ASP.NET

Cómo gestionar los datos relacionales en una caché distribuida

Iqbal Khan

 

El Microsoft .net Framework se ha vuelto popular para el desarrollo de un conjunto de aplicaciones de alta transacción incluyendo Web, arquitectura orientada a servicios (SOA), high-performance computing o cuadrícula informática y computación en nube. Todas estas arquitecturas de aplicación son escalables. Pero un obstáculo importante se produce en el almacenamiento de datos — normalmente una base de datos relacional — que no es capaz de escalar y soportar la carga de transacción mayor que el nivel de aplicación.

Como resultado, el almacenamiento en caché distribuida se está convirtiendo en muy popular porque le permite almacenar datos en caché en una caché en memoria que es mucho más rápido para acceder a cualquier base de datos. También, el almacenamiento en caché distribuida proporciona escalabilidad lineal a través de múltiples servidores de caché. Con escalabilidad lineal, puede agregar más servidores de caché para el clúster de caché distribuida si necesita manejar una carga mayor de la transacción. El autor­ganancia mental en capacidad transaccional no disminuye a medida que agregue más servidores (es una línea recta en un gráfico X-Y, donde x es el número de servidores de caché e y es la capacidad de transacción). Figura 1 muestra cómo distribuido caché encaja en una aplicación típica de ASP.NET o Windows Communication Foundation (WCF) y cómo proporciona escalabilidad lineal.


Figura 1 caché distribuida en ASP.NET o aplicaciones de Windows Communication Foundation

En este artículo, explicaré cómo los desarrolladores deben manejar relaciones de datos mientras el almacenamiento en caché de los datos.

Aunque es gran caché distribuida, un desafío que se presenta es cómo almacenar en memoria caché de datos relacionales que tiene distintas relaciones entre elementos de datos. Esto es porque un caché distribuida le proporciona una forma sencilla, como Hashtable (clave, valor) interfaz donde la "tecla" identifica un elemento almacenado en caché y «valor» es sus datos o el objeto. Sin embargo, la aplicación probablemente tiene un modelo de objeto de dominio complejo con herencia, agregación y otros tipos de relaciones.

Además, un caché distribuida almacena elementos en caché individuales por separado, que es diferente de la base de datos donde tienes relaciones entre diferentes tablas. Normalmente, no hay ninguna relación­barcos entre los diferentes en caché elementos en una caché distribuida típica. Plantea un desafío para los desarrolladores de aplicaciones .net. En otras palabras, estamos ante el reto de mucha de la información de relación dentro de la aplicación para asegurarse de que puede manejar las relaciones correctamente en la memoria caché y en el mismo tiempo Aproveche el almacenamiento en caché distribuida de seguimiento.

Explicaré cómo estas relaciones se pueden asignar y proporcionan ejemplos de código fuente. El efecto neto es que las aplicaciones no tienen que realizar un seguimiento de estas relaciones ellos mismos. Más bien, la memoria caché pueden hacerse consciente de ellos y luego tratarlos de forma independiente.

Asignación de relación de objeto es bueno

En primer lugar, asegúrese de que usted transformar los datos relacionales en un modelo de objeto de dominio. Sin este paso, usted tendría un momento difícil manejar todas las relaciones en un caché distribuida.

En cualquier aplicación. net, es muy probable que utilizas DataReader (SqlDataReader, OracleDataReader o OleDbDataReader) o DataTable para recuperar datos de la base de datos, que está muy bien. Pero muchos desarrolladores entonces directamente acceder a datos de estos objetos (especialmente DataTable) a lo largo de su aplicación. Algunos lo hacen por pereza, porque no quieren crear objetos de dominio personalizado. Otros lo hacen porque creen que pueden utilizar las capacidades de filtrado inteligentes de la DataTable.

Recomiendo encarecidamente que transforma el DataReader o sus datos­tabla en un modelo de objeto de dominio. Esto simplificar en gran medida el diseño de aplicaciones y también le permiten utilizar caché distribuido eficazmente. Y un buen caché distribuida proporciona generalmente con un tipo SQL o LINQ consultar capacidad para no perder las funciones de filtrado de DataTable.

Cuando se utiliza un objeto DataTable, usted está obligado a almacenar en caché como un elemento almacenado en caché. Tenga en cuenta que un gran número de filas de un objeto DataTable ralentiza su aplicación cuando usted la caché.

Puede transformar DataReader y DataTable en objetos de dominio manualmente o utilizar uno de los principales motores de asignación de relación de objeto (ORM). Entity Framework de Microsoft es un tal motor. Otro popular es NHibernate, que es open source. Una herramienta ORM simplifica enormemente la tarea de transformar datos relacionales en un modelo de objeto de dominio.

CacheDependency ayuda a administrar las relaciones

Con un modelo de objeto de dominio, la primera pregunta es cómo manejar todas esas relaciones en la caché. Y la respuesta es CacheDependency, que forma parte de la caché de ASP.NET y ahora se encuentra también en algunas soluciones de almacenamiento en caché distribuidos comerciales.

CacheDependency permite informar a la caché sobre difieren­tipos de relaciones entre elementos en la caché y luego deje que la caché distribuida administrar la integridad de los datos para ellos. Básicamente, un CacheDependency permite decirle a la caché que un elemento almacenado en caché depende de otro elemento almacenado en caché. Luego la caché distribuida puede realizar un seguimiento de los cambios en el elemento de destino e invalidar el elemento de origen que depende del elemento de destino.

Digamos que si el elemento de datos a depende de B, entonces si b nunca se actualiza o se quitan de la caché, la memoria caché distribuida elimina automáticamente a así. CacheDepend­profundizaci6n proporciona también en cascada capacidad, por lo que si a depende de b y b dependen de c y, a continuación, se actualiza o elimina, resulta en el b se quita automáticamente la caché distribuida. Y cuando eso sucede, también automáticamente extraído a la caché distribuida. Esto se llama la cascada CacheDependency.

Utilizo CacheDependency más adelante en este artículo para demostrar cómo manejar las relaciones en un caché distribuida.

Tres diferentes tipos de relaciones

En primer lugar, vamos a usar un modelo de datos de ejemplo para propósitos de discusión, se muestra en la figura 2.

Example Data Model for Relationships
Figura 2 modelo de datos de ejemplo para las relaciones

Como puede ver, en este modelo de datos, el cliente tiene una relación de uno a varios con orden; Producto tiene una relación de uno a varios con orden; y el cliente y el producto tienen una relación de varios a varios entre sí a través de la tabla de orden. Para nuestro modelo de datos de ejemplo, figura 3 muestra el modelo de objeto equivalente que representa las relaciones de la mismas.

Example Object Model Against the Data Model
Figura 3 modelo de objetos de ejemplo contra el modelo de datos

Antes de pasar a los detalles de los tipos de relación diferente, quiero explicar una cosa. A diferencia de la base de datos, un modelo de objetos del dominio de aplicación tiene siempre un objeto principal que ha recuperado la aplicación de la base de datos y por lo tanto, es el punto de partida para la aplicación durante una transacción en particular. Todos los demás objetos son vistos como objeto relacionado a este primario. Este concepto es válido para todos los tipos de relaciones y afecta cómo ves las relaciones en un modelo de objeto de dominio. Ahora, vamos a proceder.

Relaciones uno a uno y muchos a uno

Relaciones uno a uno y varios a uno son similares. En una relación uno a uno entre las tablas a y B, está relacionada con una fila en la tabla a sólo una fila en la tabla B. Mantenga una clave externa en la tabla a o b que es la clave principal de la otra tabla.

En el caso de una relación de varios a uno entre a y B, debe mantener la clave externa en la tabla A. Esta clave externa es la clave principal de la tabla B. En las relaciones uno a uno y muchos a uno, la clave externa tiene una restricción unique para asegurarse de que no hay duplicados.

La misma relación puede transformarse fácilmente en un modelo de objeto de dominio. El objeto principal (explicado anteriormente) mantiene una referencia al objeto relacionado. Figura 4 se muestra un ejemplo del objeto primordial mantener información de relación. Tenga en cuenta que la clase de orden contiene una referencia a la clase de cliente para indicar una relación de varios a uno. Lo mismo ocurriría incluso en una relación uno a uno.

Figura 4 almacenamiento en caché de un objeto relacionado por separado

public void CacheOrder(Order order)
{
  Cache cache = HttpRuntime.Cache;
  DateTime absolutionExpiration = Cache.NoAbsoluteExpiration;
  TimeSpan slidingExpiration = Cache.NoSlidingExpiration;
  CacheItemPriority priority = CacheItemPriority.Default;
  if (order != null) {
    // This will prevent Customer from being cached with Order
    Customer cust = order.OrderingCustomer;
    // Set orders to null so it doesn't get cached with Customer
    cust.Orders = null;
    string custKey = "Customer:CustomerId:" + cust.CustomerId;
    cache.Add(custKey, cust, null,
              absolutionExpiration,
              slidingExpiration,
              priority, null);
    // Dependency ensures order is removed if Cust updated/removed
    string[] keys = new string[1];
    keys[0] = custKey;
    CacheDependency dep = new CacheDependency(null, keys);
    string orderKey = "Order:CustomerId:" + order.CustomerId
      + ":ProductId:" + order.ProductId;
    // This will only cache Order object
    cache.Add(orderKey, order, dep,
              absolutionExpiration,
              slidingExpiration,
              priority, null);
  }
}

Relaciones uno a varios (reverso de muchos a uno)

En el caso de una relación uno a varios en la base de datos entre las tablas a y B, tabla B (es decir, el lado"varios") mantiene la clave externa, que es realmente la clave principal de la tabla A, pero sin una restricción unique en la clave externa.

En el caso de un modelo de objetos de dominio de uno a muchos, el objeto principal es el cliente y el objeto relacionado es el orden. Por lo que el objeto del cliente contiene una colección de objetos de orden. Figura 4 también se muestra un ejemplo de esta relación entre los objetos Customer y Order.

Relaciones Varios a Varios

En el caso de una relación de varios a varios entre tablas a y B, siempre hay una intermediaria tabla AB. En nuestro caso, el orden es la tabla intermediaria y tiene dos claves foráneas. Uno está en contra de la tabla cliente para representar una relación de varios a uno y el otro es contra la mesa de la orden para volver a representar una relación de varios a uno.

En caso de una relación de varios a varios, el modelo de objetos suele ser uno-a-muchos desde la perspectiva de un objeto primario (definido anteriormente) que es cliente o producto. El modelo de objetos también ahora contiene una relación de varios a uno entre el objeto intermediario (orden, en nuestro caso) y el otro objeto (producto, en nuestro caso). Figura 4 también se muestra un ejemplo de una relación de varios a varios, que en este caso es entre el cliente y el producto de objetos, con el objeto de la orden ser objeto intermediario.

Como puede ver, es el modelo de objetos desde la perspectiva del objeto de cliente que es el objeto primario, y la aplicación de lo obtenido de la base de datos. Todos relacionados con objetos son desde la perspectiva de este objeto primario. Por lo tanto un objeto de cliente tendrá una colección de objetos de orden, y cada objeto de la orden contendrá una referencia al objeto producto. El objeto del producto probablemente no contiene una colección de todos los objetos de orden pertenecientes al objeto producto debido a no es necesario aquí. Si el producto fuera el objeto principal, tendría una colección de objetos de orden, pero luego el objeto del cliente no tiene una colección de objetos de orden.

Almacenamiento en caché de estrategias para los diferentes tipos de relación

Hasta ahora, que he descrito cómo recuperar los datos de la base de datos, transformarla en un modelo de objeto de dominio y mantener las mismas relaciones en el modelo de objetos de dominio como en la base de datos — aunque desde la perspectiva del objeto primario. Pero si quiere que su aplicación a datos de la caché en una caché distribuida, debe entender cómo manejar todas estas relaciones en la caché. Voy a salir a través de cada caso.

En todos los casos, debe asegurarse que información de relación en la caché no está perdido, que afectan la integridad de los datos o su capacidad para recuperar objetos relacionados de la memoria caché más tarde.

El almacenamiento en caché de relaciones uno a uno y muchos a uno

Aquí tienes dos opciones:

  1. Caché de objetos con el objeto principal relacionados: esta opción se supone que el objeto relacionado no va a ser modificado por otro usuario mientras está en la caché, por lo que es seguro para almacenar en caché como parte del objeto principal como un elemento almacenado en caché. Si usted teme que esto no es el caso, entonces no utilice esta opción.
  2. Caché de objetos relacionados por separado: esta opción se supone que el objeto relacionado podría ser actualizado por otro usuario mientras está en la caché, así que es mejor para almacenar en caché los objetos primarios y relacionados como elementos en caché por separado. Se deben especificar claves de caché único para cada uno de los dos objetos. Además, puede utilizar la función de etiqueta de un caché distribuida para etiquetar el objeto relacionado como tener una relación con el objeto principal. Entonces usted podría buscarlo más tarde a través de la etiqueta.

El almacenamiento en caché de uno a varios relaciones

En el caso de las relaciones de uno a muchos, su objeto principal es siempre del lado"uno" (en nuestro ejemplo es el objeto del cliente). El objeto principal contiene una colección de objetos de orden. Cada colección de objetos relacionados representa una relación uno a varios. Aquí tienes tres opciones de almacenamiento en caché:

  1. Colecciones de objetos relacionados con el objeto principal de caché: este supuesto asume que objetos relacionados no actualizarse o recuperan independientemente por otro usuario en la memoria caché, por lo que es seguro para almacenar en caché como parte del objeto primario. Hacerlo mejora su rendimiento porque usted puede obtener todo en la llamada de un caché. Sin embargo, si la colección es grande (lo que significa que decenas de miles de objetos y alcanzando en megabytes de datos), no recibirá la ganancia de rendimiento.
  2. Caché de colecciones de objetos relacionados por separado: en esta situación, usted cree que otros usuarios quieran obtener las mismas colecciones de la caché; por lo tanto, tiene sentido almacenar en caché la colección de objetos relacionados por separado. Debe estructurar su clave de caché de tal manera que podrás encontrar esta colección basada en alguna información acerca de su objeto principal. Analizaré la cuestión del almacenamiento en caché de colecciones con mucho más detalle más adelante en este artículo.
  3. Todos los objetos relacionados individuales de colecciones de caché por separado: en esta situación, usted cree que podría actualizarse cada objeto de la colección relacionada por otros usuarios; por lo tanto, usted no puede mantener un objeto en la colección y debe lo caché por separado. Puede utilizar la función de etiqueta de un caché distribuida para identificar todos los objetos relacionados con su objeto principal, por lo que podrás buscarles rápidamente más tarde por.

Caché de muchos-a-muchos relaciones

Realmente no existen relaciones de muchos a muchos en un modelo de objeto de dominio. En cambio, está representadas por relaciones uno a varios, con la excepción de que el objeto intermediario (orden, en nuestro caso) contiene una referencia a otro objeto lateral (en nuestro caso, producto). En un puro uno-a-muchos, no existiría esta referencia.

Manejo de colecciones

El tema de cómo manejar colecciones es interesante, porque usted a menudo obtener una colección de objetos de la base de datos y desea poder colecciones de caché de manera eficiente.

Supongamos que tiene un escenario donde solicitar todos los clientes basados en Nueva York y no espera que los nuevos clientes a añadirse desde Nueva York en el día siguiente o en los próximos minutos. Tenga en cuenta que no caché datos durante semanas y meses, sólo por un minuto o un par de horas en la mayoría de los casos. En algunas situaciones podría caché durante muchos días.

Hay diferentes caching estrategias para almacenar en caché las colecciones, como explicaré.

Una colección de todo como un elemento de la caché

En este caso, sabes que no agregar cualquier más clientes de Nueva York y otros usuarios no acceder y modificar los datos de cliente de Nueva York durante el período en el que se almacenará en caché los datos. Por lo tanto, se puede almacenar en caché la colección completa de los clientes de Nueva York como un elemento almacenado en caché. Aquí, puede realizar los criterios de búsqueda o la parte de consulta SQL de la clave de caché. Cualquier momento que desee obtener a los clientes de Nueva York, sólo vas a la caché y decir «Dame la colección que contiene a los clientes de Nueva York.»

Figura 5 muestra cómo se almacena en caché una colección completa de objetos relacionados de la orden.

Figura 5 el almacenamiento en caché una colección relacionada por separado

public void CacheCustomer(Customer cust)
{
  Cache cache = HttpRuntime.Cache;
  DateTime absolutionExpiration = Cache.NoAbsoluteExpiration;
  TimeSpan slidingExpiration = Cache.NoSlidingExpiration;
  CacheItemPriority priority = CacheItemPriority.Default;
  if (cust != null)
  {
    string key = "Customer:CustomerId:" + cust.CustomerId;
    // Let's preserve it to cache separately
    IList<Order> orderList = cust.Orders;
    // So it doesn't get cached as part of Customer
    cust.Orders = null;
    // This will only cache Customer object
    cache.Add(key, cust, null,
              absolutionExpiration,
              slidingExpiration,
              priority, null);
    // See that this key is also Customer based
    key = "Customer:CustomerId:" + cust.CustomerId + ":Orders";
    cache.Add(key, orderList, null,
              absolutionExpiration,
              slidingExpiration,
              priority, null);
  }
}

Caché por separado cada elemento de la colección

Ahora pasemos a la segunda estrategia. En este escenario, usted u otros usuarios desean individualmente buscar y modificar a un cliente de Nueva York. Pero la anterior estrategia requeriría que todo el mundo recuperar toda la colección de la caché, modificar a esta un cliente, vuelva a colocarlo en la colección y almacenar en caché la colección nuevamente. Si haces este frecuentemente suficientemente, se convertirá en impracticable por razones de rendimiento.

Así, en este caso, no mantenga la colección de todos los clientes de Nueva York como un elemento almacenado en caché. Puedes rompen la colección y almacenan cada objeto Customer por separado en la caché. Es necesario agrupar todos estos objetos de cliente, por lo que en un momento posterior, que usted puede obtener sólo les todos de vuelta en una llamada como una colección o un IDictionary. La ventaja de este enfoque es poder recuperar y modificar objetos de cliente individuales. Figura 6 se muestra un ejemplo de cómo cada uno de los objetos relacionados en la colección de caché por separado.

Figura 6 caché por separado cada elemento de la colección

public void CacheOrdersListItems(IList<Order> ordersList)
{
  Cache cache = HttpRuntime.Cache;
  DateTime absolutionExpiration = Cache.NoAbsoluteExpiration;
  TimeSpan slidingExpiration = Cache.NoSlidingExpiration;
  CacheItemPriority priority = CacheItemPriority.Default;
  foreach (Order order in ordersList)
  {
    string key = "Order:CustomerId:" + order.CustomerId
      + ":ProductId" + order.ProductId;
    string[] keys = new string[1];
    keys[0] = key;
    // Dependency ensures Order is removed if Cust updated/removed
    CacheDependency dep = new CacheDependency(null, keys);
    Tag [] tagList = new Tag [1];
    tagList[0] = new Tag ("customerId" + order.CustomerId);
     // Tag allows us to find order with a customerId alone
     cache.Add(key, order, dep,
               absolutionExpiration,
               slidingExpiration,
               priority, null, tagList);
  }
}

Tenga en cuenta, sin embargo, que esta estrategia asume que clientes de Nueva York no se han agregado a la base de datos en el período en el que has sido caché. De lo contrario, al buscar todos los clientes de Nueva York de la caché, obtendrás sólo una lista parcial. Del mismo modo, si un cliente se elimina de la base de datos pero no de la memoria caché, obtendrás obsoleto lista de clientes.

Manejo de colecciones donde se agregan objetos o eliminados

La tercera opción para el manejo de colecciones es donde piensa nuevos clientes pueden añadirse desde Nueva York o se pueden eliminar algunos clientes existentes. En este caso, cualquier cosa que caché es sólo datos parciales o datos antiguos. Tal vez la colección tenía sólo 100 clientes y agregó dos más hoy. Esos dos no será parte de la memoria caché. Sin embargo, al buscar todos los clientes de Nueva York, desea que los datos correctos: quiere 102 resultados, no 100.

La forma de asegurar que esto ocurre es hacer una llamada a la base de datos. Obtener todos los identificadores para los clientes y hacer una comparación para ver que están en la memoria caché y cuáles no. Individualmente buscar desde la base de datos que no están en la memoria caché y agregarlos a la caché. Como puede ver, esto no es un proceso rápido; Estás realizando múltiples llamadas de base de datos y caché múltiples. Con una caché distribuida proporciona funciones básicas, si tienes una colección de 1.000 clientes y 50 nuevas se agregan, acabará haciendo 51 de base de datos de llamadas y 101 caché distribuida. En este caso, podría ser más rápido obtener la colección de la base de datos en una sola llamada.

Pero si la caché distribuida proporciona operaciones masivas, se realiza una base de datos llamada a buscar IDs, llamada una caché distribuida a ver ID existentes en la memoria caché, una llamada para añadir a todos los nuevos clientes a la caché y uno para obtener la colección completa de los clientes de la caché. Esto sería un total de una base de datos llamada y tres llamadas de caché distribuida, y que no es malo en absoluto. Y, si no nuevos clientes se agregaron (que sería el caso del 90 por ciento del tiempo), se reduciría a una base de datos llamada y dos llamadas de caché distribuida.

Rendimiento y escalabilidad

He cubierto varias situaciones que surgen cuando la aplicación recupera datos relacionales de la base de datos, transforma en un modelo de objeto de dominio y luego quiere almacenar en caché los objetos. El propósito de mi artículo es resaltar todas aquellas áreas donde las relaciones de objeto afectan a cómo se almacenar en caché los objetos y cómo posteriormente modificar o quitar de la caché.

El almacenamiento en caché distribuido es una gran manera de mejorar el rendimiento y la escalabilidad de la aplicación. Y porque las aplicaciones tratan con datos relacionales la mayoría del tiempo, espero que este artículo ha proporcionado algunos conocimientos sobre cómo deben manejar datos relacionales en una caché distribuida.

Iqbal Khan es el Evangelista de tecnología y Presidente de Alachisoft, que proporciona NCache y StorageEdge (alachisoft.com). NCache es una caché distribuida para .net y Java y mejora la escalabilidad y el rendimiento de la aplicación. StorageEdge es un proveedor RBS para SharePoint y ayuda a optimiza el almacenamiento y el rendimiento de SharePoint. Khan recibió su maestría en Ciencias de la computación de la Universidad de Indiana, Bloomington, en 1990. Llegar a él en iqbal@alachisoft.com.

Gracias al siguiente experto técnico por su ayuda en la revisión de este artículo: Damian Edwards