Compartir a través de


Guía de almacenamiento en caché

Azure Cache for Redis

El almacenamiento en caché es una técnica que tiene como objetivo mejorar el rendimiento y la escalabilidad de un sistema. Almacena en caché los datos copiando temporalmente los datos a los que se accede con frecuencia a un almacenamiento rápido que se encuentra cerca de la aplicación. Si este almacenamiento de datos rápido se encuentra más cerca de la aplicación que el primer origen, el almacenamiento en caché puede mejorar considerablemente los tiempos de respuesta de las aplicaciones cliente dado que los datos se sirven con mayor rapidez.

El almacenamiento en caché es más eficaz cuando una instancia de cliente lee repetidamente los mismos datos, especialmente si todas las condiciones siguientes se aplican al almacén de datos original:

  • Sigue siendo relativamente estático.
  • Es lento en comparación con la velocidad de la caché.
  • Está sujeto a un alto nivel de contención.
  • Está lejos cuando la latencia de red puede provocar que el acceso sea lento.

Almacenamiento en caché en aplicaciones distribuidas

Normalmente, las aplicaciones distribuidas implementan o ambas de las estrategias siguientes al almacenar datos en caché:

  • Usan una caché privada, donde los datos se mantienen localmente en el equipo que ejecuta una instancia de una aplicación o servicio.
  • Usan una caché compartida, que actúa como un origen común al que pueden acceder varios procesos y máquinas.

En ambos casos, el almacenamiento en caché se puede realizar en el lado cliente y en el lado servidor. El almacenamiento en caché del lado cliente se realiza mediante el proceso que proporciona la interfaz de usuario para un sistema, como un explorador web o una aplicación de escritorio. El proceso que proporciona los servicios empresariales que se ejecutan de forma remota realiza el almacenamiento en caché del lado servidor.

Almacenamiento en caché privado

El tipo de caché más básico es un almacén en memoria. Se mantiene en el espacio de direcciones de un único proceso y el código al que se ejecuta en ese proceso tiene acceso directo. Este tipo de caché es rápido de acceder. También puede proporcionar un medio eficaz para almacenar cantidades modestas de datos estáticos. El tamaño de una memoria caché suele estar restringido por la cantidad de memoria disponible en el equipo que hospeda el proceso.

Si necesita almacenar más información de la que es físicamente posible en la memoria, puede escribir datos almacenados en caché en el sistema de archivos local. Este proceso será más lento para acceder a los datos que se conservan en la memoria, pero debe ser más rápido y confiable que recuperar datos a través de una red.

Si tiene varias instancias de una aplicación que usa este modelo que se ejecuta simultáneamente, cada instancia de aplicación tiene su propia caché independiente que contiene su propia copia de los datos.

Piense en una memoria caché como una instantánea de los datos originales en algún momento del pasado. Si estos datos no son estáticos, es probable que diferentes instancias de aplicación contengan versiones diferentes de los datos en sus memorias caché. Por lo tanto, la misma consulta realizada por estas instancias puede devolver resultados diferentes, como se muestra en la figura 1.

Resultados del uso de una caché en memoria en diferentes instancias de una aplicación

Figura 1: Uso de una caché en memoria en instancias diferentes de una aplicación.

Almacenamiento en caché compartido

Si usa una caché compartida, puede ayudar a aliviar las preocupaciones que pueden diferir en cada caché, lo que puede ocurrir con el almacenamiento en caché en memoria. El almacenamiento en caché compartido garantiza que las distintas instancias de aplicación vean la misma vista de los datos almacenados en caché. Localiza la memoria caché en una ubicación independiente, que normalmente se hospeda como parte de un servicio independiente, como se muestra en la figura 2.

Resultados del uso de una caché compartida

Figura 2: Uso de una caché compartida.

Una ventaja importante del enfoque de almacenamiento en caché compartido es la escalabilidad que proporciona. Muchos servicios de caché compartida se implementan mediante un clúster de servidores y usan software para distribuir los datos en el clúster de forma transparente. Una instancia de aplicación simplemente envía una solicitud al servicio de caché. La infraestructura subyacente determina la ubicación de los datos almacenados en caché en el clúster. Puede escalar fácilmente la memoria caché agregando más servidores.

Hay dos desventajas principales del enfoque de almacenamiento en caché compartido:

  • La memoria caché es más lenta para acceder porque ya no se mantiene localmente en cada instancia de aplicación.
  • El requisito de implementar un servicio de caché independiente podría agregar complejidad a la solución.

Consideraciones para usar el almacenamiento en caché

En las secciones siguientes se describen con más detalle las consideraciones para diseñar y usar una memoria caché.

Decidir cuándo almacenar en caché los datos

El almacenamiento en caché puede mejorar considerablemente el rendimiento, la escalabilidad y la disponibilidad. Cuantos más datos tenga y mayor sea el número de usuarios que necesitan acceder a estos datos, mayores serán las ventajas del almacenamiento en caché. El almacenamiento en caché reduce la latencia y la contención asociadas al control de grandes volúmenes de solicitudes simultáneas en el almacén de datos original.

Por ejemplo, una base de datos podría admitir un número limitado de conexiones simultáneas. Sin embargo, recuperar datos de una caché compartida, en lugar de la base de datos subyacente, permite que una aplicación cliente acceda a estos datos incluso si el número de conexiones disponibles se agota actualmente. Además, si la base de datos deja de estar disponible, es posible que las aplicaciones cliente puedan continuar usando los datos que se mantienen en la memoria caché.

Considere la posibilidad de almacenar en caché los datos que se leen con frecuencia pero que se modifican con poca frecuencia (por ejemplo, datos que tienen una proporción mayor de operaciones de lectura que las operaciones de escritura). Sin embargo, no se recomienda usar la memoria caché como almacén autoritativo de información crítica. En su lugar, asegúrese de que todos los cambios que la aplicación no pueda permitirse perder siempre se guardan en un almacén de datos persistente. Si la memoria caché no está disponible, la aplicación todavía puede seguir funcionando mediante el almacén de datos y no perderá información importante.

Determinación de cómo almacenar en caché los datos de forma eficaz

La clave para usar una memoria caché reside eficazmente en determinar los datos más adecuados para almacenarlos en caché y almacenarlos en caché en el momento adecuado. Los datos se pueden agregar a la memoria caché a petición la primera vez que una aplicación la recupera. La aplicación debe capturar los datos solo una vez desde el almacén de datos y ese acceso posterior se puede satisfacer mediante la memoria caché.

Como alternativa, una memoria caché puede rellenarse parcialmente o completamente con datos de antemano, normalmente cuando se inicia la aplicación (un enfoque conocido como propagación). Sin embargo, es posible que no sea aconsejable implementar la propagación para una caché grande, ya que este enfoque puede imponer una carga repentina y alta en el almacén de datos original cuando la aplicación comienza a ejecutarse.

A menudo, un análisis de patrones de uso puede ayudarle a decidir si rellenar previamente una memoria caché completa o parcialmente y elegir los datos que se van a almacenar en caché. Por ejemplo, puede inicializar la memoria caché con los datos de perfil de usuario estáticos para los clientes que usan la aplicación periódicamente (quizás todos los días), pero no para los clientes que usan la aplicación solo una vez a la semana.

El almacenamiento en caché normalmente funciona bien con datos inmutables o que cambian con poca frecuencia. Algunos ejemplos incluyen información de referencia, como información de productos y precios en una aplicación de comercio electrónico, o recursos estáticos compartidos que son costosos de construir. Algunos o todos estos datos se pueden cargar en la memoria caché al iniciar la aplicación para minimizar la demanda de recursos y mejorar el rendimiento. También puede que desee tener un proceso en segundo plano que actualice periódicamente los datos de referencia en la memoria caché para asegurarse de que es up-to-date. O bien, el proceso en segundo plano puede actualizar la memoria caché cuando cambian los datos de referencia.

El almacenamiento en caché es menos útil para los datos dinámicos, aunque hay algunas excepciones a esta consideración (consulte la sección Caché de datos altamente dinámicos más adelante en este artículo para obtener más información). Cuando los datos originales cambian periódicamente, la información almacenada en caché se vuelve obsoleta rápidamente o la sobrecarga de sincronizar la memoria caché con el almacén de datos original reduce la eficacia del almacenamiento en caché.

Una caché no tiene que incluir los datos completos de una entidad. Por ejemplo, si un elemento de datos representa un objeto multivalor, como un cliente bancario con un nombre, una dirección y un saldo de cuenta, algunos de estos elementos pueden permanecer estáticos, como el nombre y la dirección. Otros elementos, como el saldo de la cuenta, pueden ser más dinámicos. En estas situaciones, puede ser útil almacenar en caché las partes estáticas de los datos y recuperar (o calcular) solo la información restante cuando sea necesario.

Se recomienda llevar a cabo pruebas de rendimiento y análisis de uso para determinar si la carga previa o a petición de la memoria caché, o bien una combinación de ambas, es adecuada. La decisión debe basarse en el patrón de volatilidad y uso de los datos. El uso de caché y el análisis de rendimiento son importantes en las aplicaciones que encuentran cargas pesadas y deben ser altamente escalables. Por ejemplo, en escenarios altamente escalables, puede inicializar la memoria caché para reducir la carga en el almacén de datos en horas punta.

El almacenamiento en caché también se puede usar para evitar cálculos repetidos mientras se ejecuta la aplicación. Si una operación transforma los datos o realiza un cálculo complicado, puede guardar los resultados de la operación en la memoria caché. Si se requiere el mismo cálculo después, la aplicación simplemente puede recuperar los resultados de la memoria caché.

Una aplicación puede modificar los datos que se mantienen en una memoria caché. Sin embargo, se recomienda pensar en la memoria caché como un almacén de datos transitorio que podría desaparecer en cualquier momento. No almacene datos valiosos solo en la memoria caché; Asegúrese de mantener también la información en el almacén de datos original. Esto significa que si la memoria caché deja de estar disponible, se minimiza la posibilidad de perder datos.

Almacenar en caché datos altamente dinámicos

Cuando se almacena información que cambia rápidamente en un almacén de datos persistente, puede imponer una sobrecarga en el sistema. Por ejemplo, considere un dispositivo que informa continuamente del estado o de alguna otra medida. Si una aplicación decide no almacenar en caché estos datos basándose en que la información almacenada en caché casi siempre estará obsoleta, la misma consideración podría ser cierta al almacenar y recuperar esta información del almacén de datos. En el tiempo necesario para guardar y capturar estos datos, es posible que haya cambiado.

En una situación como esta, tenga en cuenta las ventajas de almacenar la información dinámica directamente en la memoria caché en lugar de en el almacén de datos persistente. Si los datos no son críticos y no requieren auditoría, no importa si se pierde el cambio ocasional.

Administrar la expiración de datos en una caché

En la mayoría de los casos, los datos que se mantienen en una memoria caché son una copia de los datos que se mantienen en el almacén de datos original. Los datos del almacén de datos original pueden cambiar después de almacenarse en caché, lo que hace que los datos almacenados en caché se vuelvan obsoletos. Muchos sistemas de almacenamiento en caché permiten configurar la memoria caché para que expiren los datos y reducir el período durante el que los datos pueden estar obsoletos.

Cuando expiran los datos almacenados en caché, se quita de la memoria caché y la aplicación debe recuperar los datos del almacén de datos original (puede devolver la información recién capturada a la memoria caché). Puede establecer una directiva de expiración predeterminada al configurar la memoria caché. En muchos servicios de caché, también puede estipular el período de expiración de objetos individuales cuando los almacena mediante programación en la memoria caché. Algunas memorias caché permiten especificar el período de expiración como un valor absoluto o como un valor deslizante que hace que el elemento se quite de la memoria caché si no se tiene acceso dentro del tiempo especificado. Esta configuración invalida cualquier directiva de expiración de toda la memoria caché, pero solo para los objetos especificados.

Nota:

Tenga en cuenta el período de expiración de la memoria caché y los objetos que contiene cuidadosamente. Si lo hace demasiado corto, los objetos expirarán demasiado rápido y reducirá las ventajas de usar la memoria caché. Si hace que el período sea demasiado largo, corre el riesgo de que los datos se vuelvan obsoletos.

También es posible que la memoria caché se rellene si los datos pueden permanecer residentes durante mucho tiempo. En este caso, las solicitudes para agregar nuevos elementos a la memoria caché pueden provocar que algunos elementos se quiten forzosamente en un proceso conocido como expulsión. Normalmente, los servicios de caché expulsan los datos por lo menos usados (LRU), pero normalmente puede invalidar esta directiva e impedir que los elementos se expulsen. Sin embargo, si adopta este enfoque, corre el riesgo de superar la memoria que está disponible en la memoria caché. Se producirá un error en una aplicación que intente agregar un elemento a la memoria caché con una excepción.

Algunas implementaciones de almacenamiento en caché podrían proporcionar directivas de expulsión adicionales. Hay varios tipos de directivas de expulsión. Estos incluyen:

  • Una directiva usada más recientemente (en la expectativa de que los datos no se vuelvan a requerir).
  • Una directiva primero en salir (los datos más antiguos se expulsan primero).
  • Una directiva de eliminación explícita basada en un evento desencadenado (como los datos que se están modificando).

Invalidar datos en una caché del lado cliente

Los datos que se mantienen en una caché del lado cliente se consideran generalmente fuera de los auspicios del servicio que proporciona los datos al cliente. Un servicio no puede forzar directamente a un cliente a agregar o quitar información de una caché del lado cliente.

Esto significa que es posible que un cliente que use una memoria caché mal configurada para seguir usando información obsoleta. Por ejemplo, si las directivas de expiración de la memoria caché no se implementan correctamente, un cliente podría usar información obsoleta almacenada en caché localmente cuando la información del origen de datos original ha cambiado.

Si crea una aplicación web que sirve datos a través de una conexión HTTP, puede forzar implícitamente a un cliente web (como un explorador o proxy web) para capturar la información más reciente. Puede hacerlo si un recurso se actualiza mediante un cambio en el URI de ese recurso. Los clientes web suelen usar el URI de un recurso como clave en la memoria caché del lado cliente, por lo que si cambia el URI, el cliente web omite las versiones almacenadas anteriormente en caché de un recurso y captura la nueva versión en su lugar.

Administración de la simultaneidad en una memoria caché

Las memorias caché a menudo están diseñadas para compartirse mediante varias instancias de una aplicación. Cada instancia de aplicación puede leer y modificar datos en la memoria caché. Por lo tanto, los mismos problemas de simultaneidad que surgen con cualquier almacén de datos compartido también se aplican a una memoria caché. En una situación en la que una aplicación necesita modificar los datos que se mantienen en la memoria caché, es posible que tenga que asegurarse de que las actualizaciones realizadas por una instancia de la aplicación no sobrescribirán los cambios realizados por otra instancia.

En función de la naturaleza de los datos y de la probabilidad de colisiones, puede adoptar uno de estos dos enfoques para la simultaneidad:

  • Optimista. Inmediatamente antes de actualizar los datos, la aplicación comprueba si los datos de la caché han cambiado desde que se recuperó. Si los datos siguen siendo los mismos, se puede realizar el cambio. De lo contrario, la aplicación tiene que decidir si se va a actualizar. (La lógica de negocios que impulsa esta decisión será específica de la aplicación). Este enfoque es adecuado para situaciones en las que las actualizaciones son poco frecuentes o donde es poco probable que se produzcan colisiones.
  • Pesimista. Cuando recupera los datos, la aplicación la bloquea en la memoria caché para evitar que otra instancia lo cambie. Este proceso garantiza que no se puedan producir colisiones, pero también pueden bloquear otras instancias que necesiten procesar los mismos datos. La simultaneidad pesimista puede afectar a la escalabilidad de una solución y solo se recomienda para las operaciones de corta duración. Este enfoque puede ser adecuado para situaciones en las que las colisiones son más probables, especialmente si una aplicación actualiza varios elementos en la memoria caché y debe asegurarse de que estos cambios se aplican de forma coherente.

Implementación de alta disponibilidad y escalabilidad, y mejora del rendimiento

Evite usar una memoria caché como repositorio principal de datos; este es el rol del almacén de datos original desde el que se rellena la memoria caché. El almacén de datos original es responsable de garantizar la persistencia de los datos.

Tenga cuidado de no introducir dependencias críticas sobre la disponibilidad de un servicio de caché compartido en las soluciones. Una aplicación debe poder seguir funcionando si el servicio que proporciona la memoria caché compartida no está disponible. La aplicación no debe dejar de responder ni producir un error mientras espera a que se reanude el servicio de caché.

Por lo tanto, la aplicación debe estar preparada para detectar la disponibilidad del servicio de caché y revertir al almacén de datos original si no se puede acceder a la memoria caché. El patrón deCircuit-Breaker es útil para controlar este escenario. El servicio que proporciona la memoria caché se puede recuperar y, una vez que esté disponible, la memoria caché se puede repoblar a medida que se leen los datos del almacén de datos original, siguiendo una estrategia como el patrón Cache-aside.

Sin embargo, la escalabilidad del sistema puede verse afectada si la aplicación vuelve al almacén de datos original cuando la memoria caché no está disponible temporalmente. Aunque el almacén de datos se está recuperando, el almacén de datos original podría estar desbordado con solicitudes de datos, lo que da lugar a tiempos de espera y conexiones con errores.

Considere la posibilidad de implementar una caché privada local en cada instancia de una aplicación, junto con la caché compartida a la que acceden todas las instancias de aplicación. Cuando la aplicación recupera un elemento, puede comprobar primero en su caché local, después en la caché compartida y, por último, en el almacén de datos original. La caché local se puede rellenar mediante los datos de la caché compartida o en la base de datos si la caché compartida no está disponible.

Este enfoque requiere una configuración cuidadosa para evitar que la caché local se vuelva demasiado obsoleta con respecto a la caché compartida. Sin embargo, la caché local actúa como un búfer si no se puede acceder a la caché compartida. En la figura 3 se muestra esta estructura.

Uso de una caché privada local con una caché compartida

Figura 3: Uso de una caché privada local con una caché compartida.

Para admitir cachés de gran tamaño que contienen datos relativamente de larga duración, algunos servicios de caché proporcionan una opción de alta disponibilidad que implementa la conmutación automática por error si la memoria caché deja de estar disponible. Este enfoque suele implicar la replicación de los datos almacenados en caché almacenados en un servidor de caché principal en un servidor de caché secundario y cambiar al servidor secundario si se produce un error en el servidor principal o se pierde la conectividad.

Para reducir la latencia asociada a la escritura en varios destinos, la replicación en el servidor secundario puede producirse de forma asincrónica cuando los datos se escriben en la memoria caché del servidor principal. Este enfoque conduce a la posibilidad de que se pierda información almacenada en caché si se produce un error, pero la proporción de estos datos debe ser pequeña, en comparación con el tamaño general de la memoria caché.

Si una memoria caché compartida es grande, puede resultar útil crear particiones de los datos almacenados en caché entre nodos para reducir las posibilidades de contención y mejorar la escalabilidad. Muchas cachés compartidas admiten la capacidad de agregar (y quitar) de forma dinámica los nodos y reequilibrar los datos entre particiones. Este enfoque podría implicar la agrupación en clústeres, en la que la colección de nodos se presenta a las aplicaciones cliente como una caché única sin problemas. Sin embargo, internamente, los datos se dispersan entre nodos después de una estrategia de distribución predefinida que equilibra la carga uniformemente. Para obtener más información sobre las posibles estrategias de creación de particiones, consulte Guía de creación de particiones de datos.

La agrupación en clústeres también puede aumentar la disponibilidad de la memoria caché. Si se produce un error en un nodo, el resto de la memoria caché sigue siendo accesible. La agrupación en clústeres se usa con frecuencia junto con la replicación y la conmutación por error. Cada nodo se puede replicar y la réplica se puede conectar rápidamente si se produce un error en el nodo.

Es probable que muchas operaciones de lectura y escritura impliquen valores o objetos de datos únicos. Sin embargo, en ocasiones podría ser necesario almacenar o recuperar grandes volúmenes de datos rápidamente. Por ejemplo, la propagación de una memoria caché podría implicar la escritura de cientos o miles de elementos en la memoria caché. Es posible que una aplicación también necesite recuperar un gran número de elementos relacionados de la memoria caché como parte de la misma solicitud.

Muchas cachés a gran escala proporcionan operaciones por lotes para estos fines. Esto permite a una aplicación cliente empaquetar un gran volumen de elementos en una sola solicitud y reduce la sobrecarga asociada a realizar un gran número de solicitudes pequeñas.

Almacenamiento en caché y coherencia final

Para que el patrón cache-aside funcione, la instancia de la aplicación que rellena la caché debe tener acceso a la versión más reciente y coherente de los datos. En un sistema que implementa la coherencia final (por ejemplo, un almacén de datos replicado), esto podría no ser el caso.

Una instancia de una aplicación podría modificar un elemento de datos e invalidar la versión almacenada en caché de ese elemento. Otra instancia de la aplicación podría intentar leer este elemento de una memoria caché, lo que provoca un error en la memoria caché, por lo que lee los datos del almacén de datos y lo agrega a la memoria caché. Sin embargo, si el almacén de datos no se ha sincronizado completamente con las otras réplicas, la instancia de la aplicación podría leer y rellenar la memoria caché con el valor anterior.

Para obtener más información sobre cómo controlar la coherencia de los datos, consulte el manual de coherencia de datos.

Protección de datos almacenados en caché

Independientemente del servicio de caché que use, considere cómo proteger los datos que se mantienen en la memoria caché frente al acceso no autorizado. Hay dos preocupaciones principales:

  • Privacidad de los datos en la memoria caché.
  • La privacidad de los datos a medida que fluye entre la memoria caché y la aplicación que usa la memoria caché.

Para proteger los datos de la memoria caché, el servicio de caché podría implementar un mecanismo de autenticación que requiera que las aplicaciones especifiquen lo siguiente:

  • Qué identidades pueden acceder a los datos de la memoria caché.
  • Qué operaciones (lectura y escritura) que estas identidades pueden realizar.

Para reducir la sobrecarga asociada a la lectura y escritura de datos, después de conceder a una identidad acceso de escritura o lectura a la memoria caché, esa identidad puede usar cualquier dato de la memoria caché.

Si necesita restringir el acceso a subconjuntos de los datos almacenados en caché, puede realizar una de las acciones siguientes:

  • Divida la memoria caché en particiones (mediante diferentes servidores de caché) y conceda acceso solo a identidades para las particiones que deben poder usar.
  • Cifre los datos de cada subconjunto mediante claves diferentes y proporcione las claves de cifrado solo a las identidades que deben tener acceso a cada subconjunto. Es posible que una aplicación cliente todavía pueda recuperar todos los datos de la memoria caché, pero solo podrá descifrar los datos para los que tiene las claves.

También debe proteger los datos a medida que fluyen dentro y fuera de la memoria caché. Para ello, depende de las características de seguridad proporcionadas por la infraestructura de red que usan las aplicaciones cliente para conectarse a la memoria caché. Si la memoria caché se implementa mediante un servidor local dentro de la misma organización que hospeda las aplicaciones cliente, es posible que el aislamiento de la propia red no requiera que realice pasos adicionales. Si la memoria caché se encuentra de forma remota y requiere una conexión TCP o HTTP a través de una red pública (como Internet), considere la posibilidad de implementar SSL.

Consideraciones para implementar el almacenamiento en caché en Azure

Azure Cache for Redis es una implementación de la caché de Redis de código abierto que se ejecuta como servicio en un centro de datos de Azure. Proporciona un servicio de almacenamiento en caché al que se puede acceder desde cualquier aplicación de Azure, tanto si la aplicación se implementa como un servicio en la nube, un sitio web o dentro de una máquina virtual de Azure. Las aplicaciones cliente pueden compartir las memorias caché que tienen la clave de acceso adecuada.

Azure Cache for Redis es una solución de almacenamiento en caché de alto rendimiento que proporciona disponibilidad, escalabilidad y seguridad. Normalmente se ejecuta como un servicio distribuido entre una o varias máquinas dedicadas. Intenta almacenar tanta información como pueda en la memoria para garantizar un acceso rápido. Esta arquitectura está pensada para proporcionar baja latencia y alto rendimiento al reducir la necesidad de realizar operaciones de E/S lentas.

Azure Cache for Redis es compatible con muchas de las distintas API que usan las aplicaciones cliente. Si tiene aplicaciones existentes que ya usan Azure Cache for Redis que se ejecutan de forma local, Azure Cache for Redis proporciona una ruta de migración rápida para almacenar en caché en la nube.

Características de Redis

Redis es más que un servidor de caché simple. Proporciona una base de datos distribuida en memoria con un amplio conjunto de comandos que admite muchos escenarios comunes. Estos se describen más adelante en este documento, en la sección Uso del almacenamiento en caché de Redis. En esta sección se resumen algunas de las características clave que proporciona Redis.

Redis como base de datos en memoria

Redis admite operaciones de lectura y escritura. En Redis, las escrituras se pueden proteger frente a errores del sistema, ya sea almacenando periódicamente en un archivo de instantánea local o en un archivo de registro de solo anexión. Esta situación no es el caso en muchas memorias caché, que se deben considerar almacenes de datos transitorios.

Todas las escrituras son asincrónicas y no impiden que los clientes lean y escriban datos. Cuando Redis comienza a ejecutarse, lee los datos del archivo de registro o instantánea y lo usa para construir la memoria caché en memoria. Para más información, consulte Persistencia de Redis en el sitio web de Redis.

Nota:

Redis no garantiza que todas las escrituras se guarden si se produce un error catastrófico, pero en el peor de los casos podría perder solo unos segundos de datos. Recuerde que una memoria caché no está pensada para actuar como origen de datos autoritativo y es responsabilidad de las aplicaciones que usan la memoria caché para asegurarse de que los datos críticos se guardan correctamente en un almacén de datos adecuado. Para obtener más información, consulte el patrón Cache-aside.

Tipos de datos de Redis

Redis es un almacén de clave-valor, donde los valores pueden contener tipos simples o estructuras de datos complejas, como hashes, listas y conjuntos. Admite un conjunto de operaciones atómicas en estos tipos de datos. Las claves pueden ser permanentes o etiquetadas con un período de vida limitado, en el que la clave y su valor correspondiente se quitan automáticamente de la memoria caché. Para obtener más información sobre las claves y los valores de Redis, visite la página Introducción a los tipos de datos y abstracciones de Redis en el sitio web de Redis.

Replicación y agrupación en clústeres de Redis

Redis admite la replicación principal o subordinada para ayudar a garantizar la disponibilidad y mantener el rendimiento. Las operaciones de escritura en un nodo principal de Redis se replican en uno o varios nodos subordinados. Las operaciones de lectura pueden ser atendidas por el servidor principal o cualquiera de los subordinados.

Si tiene una partición de red, los subordinados pueden seguir sirviendo datos y volver a sincronizarlos de forma transparente con el principal cuando se restablezca la conexión. Para obtener más información, visite la página Replicación en el sitio web de Redis.

Redis también proporciona agrupación en clústeres, que permite particionar datos de forma transparente en particiones entre servidores y distribuir la carga. Esta característica mejora la escalabilidad, ya que se pueden agregar nuevos servidores de Redis y los datos se vuelven a particionar a medida que aumenta el tamaño de la memoria caché.

Además, cada servidor del clúster se puede replicar mediante la replicación principal o subordinada. Esto garantiza la disponibilidad en cada nodo del clúster. Para más información sobre la agrupación en clústeres y el particionamiento, visite la página del tutorial del clúster de Redis en el sitio web de Redis.

Uso de memoria de Redis

Una caché de Redis tiene un tamaño finito que depende de los recursos disponibles en el equipo host. Al configurar un servidor de Redis, puede especificar la cantidad máxima de memoria que puede usar. También puede configurar una clave en una caché de Redis para tener una fecha de expiración, después de la cual se quita automáticamente de la memoria caché. Esta característica puede ayudar a evitar que la memoria caché en memoria se llene con datos antiguos o obsoletos.

A medida que la memoria se llena, Redis puede expulsar automáticamente las claves y sus valores siguiendo una serie de directivas. El valor predeterminado es LRU (menos usado recientemente), pero también puede seleccionar otras directivas, como expulsar claves aleatoriamente o desactivar la expulsión por completo (en cuyo caso se intenta agregar elementos a la memoria caché si está lleno). La página Uso de Redis como caché LRU proporciona más información.

Transacciones y lotes de Redis

Redis permite a una aplicación cliente enviar una serie de operaciones que leen y escriben datos en la memoria caché como una transacción atómica. Se garantiza que todos los comandos de la transacción se ejecuten secuencialmente y no se intermute ningún comando emitido por otros clientes simultáneos entre ellos.

Sin embargo, estas no son transacciones verdaderas como una base de datos relacional las realizaría. El procesamiento de transacciones consta de dos fases: la primera es cuando se ponen en cola los comandos y la segunda es cuando se ejecutan los comandos. Durante la fase de puesta en cola de comandos, el cliente envía los comandos que componen la transacción. Si se produce algún tipo de error en este punto (como un error de sintaxis o el número incorrecto de parámetros), Redis se niega a procesar toda la transacción y la descarta.

Durante la fase de ejecución, Redis realiza cada comando en cola en secuencia. Si se produce un error en un comando durante esta fase, Redis continúa con el siguiente comando en cola y no revierte los efectos de los comandos que ya se han ejecutado. Esta forma simplificada de transacción ayuda a mantener el rendimiento y a evitar problemas de rendimiento causados por la contención.

Redis implementa una forma de bloqueo optimista para ayudar a mantener la coherencia. Para obtener información detallada sobre las transacciones y el bloqueo con Redis, visite la página Transacciones en el sitio web de Redis.

Redis también admite el procesamiento por lotes no transaccional de solicitudes. El protocolo Redis que los clientes usan para enviar comandos a un servidor de Redis permite a un cliente enviar una serie de operaciones como parte de la misma solicitud. Esto puede ayudar a reducir la fragmentación de paquetes en la red. Cuando se procesa el lote, se realiza cada comando. Si alguno de estos comandos tiene un formato incorrecto, se rechazará (lo que no sucede con una transacción), pero se realizarán los comandos restantes. Tampoco hay ninguna garantía sobre el orden en que se procesarán los comandos del lote.

Seguridad de Redis

Redis se centra exclusivamente en proporcionar acceso rápido a los datos y está diseñado para ejecutarse dentro de un entorno de confianza al que solo pueden acceder los clientes de confianza. Redis admite un modelo de seguridad limitado basado en la autenticación de contraseñas. (Es posible quitar completamente la autenticación, aunque no se recomienda esto).

Todos los clientes autenticados comparten la misma contraseña global y tienen acceso a los mismos recursos. Si necesita una seguridad de inicio de sesión más completa, debe implementar su propia capa de seguridad delante del servidor de Redis y todas las solicitudes de cliente deben pasar a través de esta capa adicional. Redis no debe exponerse directamente a clientes que no son de confianza o no autenticados.

Puede restringir el acceso a los comandos deshabilitandolos o cambiando su nombre (y proporcionando solo clientes con privilegios con los nuevos nombres).

Redis no admite directamente ninguna forma de cifrado de datos, por lo que todas las aplicaciones cliente deben realizar la codificación. Además, Redis no proporciona ninguna forma de seguridad de transporte. Si necesita proteger los datos a medida que fluye a través de la red, se recomienda implementar un proxy SSL.

Para obtener más información, visite la página de seguridad de Redis en el sitio web de Redis.

Nota:

Azure Cache for Redis proporciona su propia capa de seguridad a través de la cual se conectan los clientes. Los servidores de Redis subyacentes no se exponen a la red pública.

Azure Redis Cache

Azure Cache for Redis proporciona acceso a los servidores de Redis hospedados en un centro de datos de Azure. Actúa como fachada que proporciona control de acceso y seguridad. Puede aprovisionar una caché mediante Azure Portal.

El portal proporciona una serie de configuraciones predefinidas. Estos intervalos van desde una caché de 53 GB que se ejecuta como un servicio dedicado que admite comunicaciones SSL (para privacidad) y replicación maestra/subordinada con un acuerdo de nivel de servicio (SLA) de 99,9% disponibilidad, hasta una caché de 250 MB sin replicación (sin garantías de disponibilidad) que se ejecutan en hardware compartido.

Con Azure Portal, también puede configurar la directiva de expulsión de la caché y controlar el acceso a la caché agregando usuarios a los roles proporcionados. Estos roles, que definen las operaciones que los miembros pueden realizar, incluyen Propietario, Colaborador y Lector. Por ejemplo, los miembros del rol Propietario tienen control total sobre la memoria caché (incluida la seguridad) y su contenido, los miembros del rol Colaborador pueden leer y escribir información en la memoria caché, y los miembros del rol Lector solo pueden recuperar datos de la memoria caché.

La mayoría de las tareas administrativas se realizan a través de Azure Portal. Por este motivo, muchos de los comandos administrativos que están disponibles en la versión estándar de Redis no están disponibles, incluida la capacidad de modificar la configuración mediante programación, apagar el servidor de Redis, configurar subordinados adicionales o guardar datos forzadamente en el disco.

Azure Portal incluye una pantalla gráfica cómoda que le permite supervisar el rendimiento de la memoria caché. Por ejemplo, puede ver el número de conexiones que se realizan, el número de solicitudes que se realizan, el volumen de lecturas y escrituras, y el número de aciertos de caché frente a errores de caché. Con esta información, puede determinar la eficacia de la memoria caché y, si es necesario, cambiar a otra configuración o cambiar la directiva de expulsión.

Además, puede crear alertas que envíen mensajes de correo electrónico a un administrador si una o varias métricas críticas están fuera de un intervalo esperado. Por ejemplo, es posible que quiera alertar a un administrador si el número de errores de caché supera un valor especificado en la última hora, ya que significa que la memoria caché puede ser demasiado pequeña o que los datos se expulsen demasiado rápidamente.

También puede supervisar el uso de CPU, memoria y red para la memoria caché.

Para más información y ejemplos que muestran cómo crear y configurar una instancia de Azure Cache for Redis, visite la página Lap around Azure Cache for Redis (Lap around Azure Cache for Redis) en el blog de Azure.

Almacenamiento en caché del estado de sesión y salida HTML

Si compila ASP.NET aplicaciones web que se ejecutan mediante roles web de Azure, puede guardar la información de estado de sesión y la salida HTML en una instancia de Azure Cache for Redis. El proveedor de estado de sesión para Azure Cache for Redis le permite compartir información de sesión entre diferentes instancias de una aplicación web de ASP.NET y es muy útil en situaciones de granja de servidores web en las que la afinidad de cliente-servidor no está disponible y el almacenamiento en caché de datos de sesión en memoria no sería adecuado.

El uso del proveedor de estado de sesión con Azure Cache for Redis ofrece varias ventajas, entre las que se incluyen:

  • Uso compartido del estado de sesión con un gran número de instancias de ASP.NET aplicaciones web.
  • Proporcionar escalabilidad mejorada.
  • Compatibilidad con el acceso controlado y simultáneo a los mismos datos de estado de sesión para varios lectores y un único escritor.
  • Uso de la compresión para ahorrar memoria y mejorar el rendimiento de la red.

Para más información, consulte ASP.NET proveedor de estado de sesión para Azure Cache for Redis.

Nota:

No use el proveedor de estado de sesión para Azure Cache for Redis con ASP.NET aplicaciones que se ejecutan fuera del entorno de Azure. La latencia de acceso a la caché desde fuera de Azure puede eliminar las ventajas de rendimiento del almacenamiento en caché de datos.

Del mismo modo, el proveedor de caché de salida para Azure Cache for Redis permite guardar las respuestas HTTP generadas por una aplicación web de ASP.NET. El uso del proveedor de caché de salida con Azure Cache for Redis puede mejorar los tiempos de respuesta de las aplicaciones que representan una salida HTML compleja. Las instancias de aplicación que generan respuestas similares pueden usar los fragmentos de salida compartidos en la memoria caché en lugar de generar esta salida HTML afresh. Para más información, consulte ASP.NET proveedor de caché de salida para Azure Cache for Redis.

Creación de una caché de Redis personalizada

Azure Cache for Redis actúa como fachada a los servidores de Redis subyacentes. Si necesita una configuración avanzada que no esté cubierta por la caché de Azure Redis (por ejemplo, una caché mayor que 53 GB), puede compilar y hospedar sus propios servidores de Redis mediante Azure Virtual Machines.

Se trata de un proceso potencialmente complejo, ya que es posible que tenga que crear varias máquinas virtuales para que actúen como nodos principales y subordinados si desea implementar la replicación. Además, si desea crear un clúster, necesitará varios servidores principales y subordinados. Una topología de replicación en clúster mínima que proporciona un alto grado de disponibilidad y escalabilidad consta de al menos seis máquinas virtuales organizadas como tres pares de servidores primarios o subordinados (un clúster debe contener al menos tres nodos principales).

Cada par principal o subordinado debe encontrarse cerca para minimizar la latencia. Sin embargo, cada conjunto de pares se puede ejecutar en diferentes centros de datos de Azure ubicados en regiones diferentes, si desea buscar datos almacenados en caché cerca de las aplicaciones que es más probable que lo usen. Para ver un ejemplo de creación y configuración de un nodo de Redis que se ejecuta como una máquina virtual de Azure, consulte Ejecución de Redis en una máquina virtual CentOS Linux en Azure.

Nota:

Si implementa su propia caché de Redis de esta manera, es responsable de supervisar, administrar y proteger el servicio.

Creación de particiones de una caché de Redis

La creación de particiones de la memoria caché implica dividir la memoria caché entre varios equipos. Esta estructura le ofrece varias ventajas sobre el uso de un único servidor de caché, entre los que se incluyen:

  • Crear una memoria caché que sea mucho mayor de lo que se puede almacenar en un solo servidor.
  • Distribuir datos entre servidores y mejorar la disponibilidad. Si se produce un error en un servidor o se vuelve inaccesible, los datos que contiene no están disponibles, pero todavía se puede acceder a los datos de los servidores restantes. En el caso de una memoria caché, esto no es fundamental porque los datos almacenados en caché son solo una copia transitoria de los datos que se mantienen en una base de datos. Los datos almacenados en caché en un servidor que se vuelven inaccesibles se pueden almacenar en caché en un servidor diferente en su lugar.
  • Distribuir la carga entre servidores, lo que mejora el rendimiento y la escalabilidad.
  • Geolocalización de datos cerca de los usuarios que acceden a ellos, lo que reduce la latencia.

En el caso de una memoria caché, la forma más común de particionamiento es particionamiento. En esta estrategia, cada partición (o partición) es una caché de Redis de su propio derecho. Los datos se dirigen a una partición específica mediante la lógica de particionamiento, que puede usar una variedad de enfoques para distribuir los datos. El patrón de particionamiento proporciona más información sobre cómo implementar el particionamiento.

Para implementar la creación de particiones en una caché de Redis, puede adoptar uno de los métodos siguientes:

  • Enrutamiento de consultas del lado servidor. En esta técnica, una aplicación cliente envía una solicitud a cualquiera de los servidores de Redis que componen la memoria caché (probablemente el servidor más cercano). Cada servidor de Redis almacena metadatos que describen la partición que contiene y también contiene información sobre qué particiones se encuentran en otros servidores. El servidor de Redis examina la solicitud de cliente. Si se puede resolver localmente, realizará la operación solicitada. De lo contrario, reenviará la solicitud al servidor adecuado. La agrupación en clústeres de Redis implementa este modelo y se describe con más detalle en la página del tutorial del clúster de Redis en el sitio web de Redis. La agrupación en clústeres de Redis es transparente para las aplicaciones cliente y se pueden agregar servidores de Redis adicionales al clúster (y los datos repartidos) sin necesidad de volver a configurar los clientes.
  • Creación de particiones del lado cliente. En este modelo, la aplicación cliente contiene lógica (posiblemente en forma de biblioteca) que enruta las solicitudes al servidor de Redis adecuado. Este enfoque se puede usar con Azure Cache for Redis. Cree varias instancias de Azure Cache for Redis (una para cada partición de datos) e implemente la lógica del lado cliente que enruta las solicitudes a la caché correcta. Si cambia el esquema de partición (si se crean instancias adicionales de Azure Cache for Redis, por ejemplo), es posible que sea necesario volver a configurar las aplicaciones cliente.
  • Creación de particiones asistidas por proxy. En este esquema, las aplicaciones cliente envían solicitudes a un servicio proxy intermediario que entiende cómo se particionan los datos y, a continuación, enrutan la solicitud al servidor redis adecuado. Este enfoque también se puede usar con Azure Cache for Redis; el servicio proxy se puede implementar como un servicio en la nube de Azure. Este enfoque requiere un nivel adicional de complejidad para implementar el servicio y las solicitudes pueden tardar más tiempo en realizarse que el uso de particiones del lado cliente.

La página Creación de particiones: cómo dividir datos entre varias instancias de Redis en el sitio web de Redis proporciona más información sobre la implementación de particiones con Redis.

Implementación de aplicaciones cliente de Caché en Redis

Redis admite aplicaciones cliente escritas en numerosos lenguajes de programación. Si compila aplicaciones nuevas mediante .NET Framework, se recomienda usar la biblioteca cliente StackExchange.Redis. Esta biblioteca proporciona un modelo de objetos de .NET Framework que abstrae los detalles para conectarse a un servidor de Redis, enviar comandos y recibir respuestas. Está disponible en Visual Studio como un paquete NuGet. Puede usar esta misma biblioteca para conectarse a una instancia de Azure Cache for Redis o a una caché de Redis personalizada hospedada en una máquina virtual.

Para conectarse a un servidor de Redis, use el método estático Connect de la ConnectionMultiplexer clase . La conexión que crea este método está diseñada para usarse durante toda la vigencia de la aplicación cliente, y varias subprocesos simultáneos pueden usar la misma conexión. No vuelva a conectarse y desconecte cada vez que realice una operación de Redis porque esto puede degradar el rendimiento.

Puede especificar los parámetros de conexión, como la dirección del host de Redis y la contraseña. Si usa Azure Cache for Redis, la contraseña es la clave principal o secundaria que se genera para Azure Cache for Redis mediante Azure Portal.

Después de conectarse al servidor de Redis, puede obtener un identificador en la base de datos de Redis que actúa como caché. La conexión de Redis proporciona el GetDatabase método para hacerlo. A continuación, puede recuperar elementos de la memoria caché y almacenar datos en la memoria caché mediante los StringGet métodos y StringSet . Estos métodos esperan una clave como parámetro y devuelven el elemento en la memoria caché que tiene un valor coincidente (StringGet) o agregan el elemento a la memoria caché con esta clave (StringSet).

En función de la ubicación del servidor de Redis, muchas operaciones pueden incurrir en cierta latencia mientras se transmite una solicitud al servidor y se devuelve una respuesta al cliente. La biblioteca StackExchange proporciona versiones asincrónicas de muchos de los métodos que expone para ayudar a las aplicaciones cliente a seguir respondiendo. Estos métodos admiten el patrón asincrónico basado en tareas en .NET Framework.

El fragmento de código siguiente muestra un método denominado RetrieveItem. Ilustra una implementación del patrón cache-aside basado en Redis y la biblioteca StackExchange. El método toma un valor de clave de cadena e intenta recuperar el elemento correspondiente de la caché de Redis llamando al StringGetAsync método (la versión asincrónica de StringGet).

Si no se encuentra el elemento, se captura del origen de datos subyacente mediante el GetItemFromDataSourceAsync método (que es un método local y no forma parte de la biblioteca StackExchange). A continuación, se agrega a la memoria caché mediante el StringSetAsync método para que se pueda recuperar más rápidamente la próxima vez.

// Connect to the Azure Redis cache
ConfigurationOptions config = new ConfigurationOptions();
config.EndPoints.Add("<your DNS name>.redis.cache.windows.net");
config.Password = "<Redis cache key from management portal>";
ConnectionMultiplexer redisHostConnection = ConnectionMultiplexer.Connect(config);
IDatabase cache = redisHostConnection.GetDatabase();
...
private async Task<string> RetrieveItem(string itemKey)
{
    // Attempt to retrieve the item from the Redis cache
    string itemValue = await cache.StringGetAsync(itemKey);

    // If the value returned is null, the item was not found in the cache
    // So retrieve the item from the data source and add it to the cache
    if (itemValue == null)
    {
        itemValue = await GetItemFromDataSourceAsync(itemKey);
        await cache.StringSetAsync(itemKey, itemValue);
    }

    // Return the item
    return itemValue;
}

Los StringGet métodos y StringSet no están restringidos a recuperar ni almacenar valores de cadena. Pueden tomar cualquier elemento que se serialice como una matriz de bytes. Si necesita guardar un objeto .NET, puede serializarlo como un flujo de bytes y usar el StringSet método para escribirlo en la memoria caché.

Del mismo modo, puede leer un objeto de la memoria caché mediante el StringGet método y deserializarlo como un objeto .NET. El código siguiente muestra un conjunto de métodos de extensión para la interfaz IDatabase (el GetDatabase método de una conexión de Redis devuelve un IDatabase objeto) y algún código de ejemplo que usa estos métodos para leer y escribir un BlogPost objeto en la memoria caché:

public static class RedisCacheExtensions
{
    public static async Task<T> GetAsync<T>(this IDatabase cache, string key)
    {
        return Deserialize<T>(await cache.StringGetAsync(key));
    }

    public static async Task<object> GetAsync(this IDatabase cache, string key)
    {
        return Deserialize<object>(await cache.StringGetAsync(key));
    }

    public static async Task SetAsync(this IDatabase cache, string key, object value)
    {
        await cache.StringSetAsync(key, Serialize(value));
    }

    static byte[] Serialize(object o)
    {
        byte[] objectDataAsStream = null;

        if (o != null)
        {
            var jsonString = JsonSerializer.Serialize(o);
            objectDataAsStream = Encoding.ASCII.GetBytes(jsonString);
        }

        return objectDataAsStream;
    }

    static T Deserialize<T>(byte[] stream)
    {
        T result = default(T);

        if (stream != null)
        {
            var jsonString = Encoding.ASCII.GetString(stream);
            result = JsonSerializer.Deserialize<T>(jsonString);
        }

        return result;
    }
}

En el código siguiente se muestra un método denominado RetrieveBlogPost que usa estos métodos de extensión para leer y escribir un objeto serializable BlogPost en la memoria caché siguiendo el patrón cache-aside:

// The BlogPost type
public class BlogPost
{
    private HashSet<string> tags;

    public BlogPost(int id, string title, int score, IEnumerable<string> tags)
    {
        this.Id = id;
        this.Title = title;
        this.Score = score;
        this.tags = new HashSet<string>(tags);
    }

    public int Id { get; set; }
    public string Title { get; set; }
    public int Score { get; set; }
    public ICollection<string> Tags => this.tags;
}
...
private async Task<BlogPost> RetrieveBlogPost(string blogPostKey)
{
    BlogPost blogPost = await cache.GetAsync<BlogPost>(blogPostKey);
    if (blogPost == null)
    {
        blogPost = await GetBlogPostFromDataSourceAsync(blogPostKey);
        await cache.SetAsync(blogPostKey, blogPost);
    }

    return blogPost;
}

Redis admite la canalización de comandos si una aplicación cliente envía varias solicitudes asincrónicas. Redis puede multiplexar las solicitudes con la misma conexión en lugar de recibir y responder a comandos en una secuencia estricta.

Este enfoque ayuda a reducir la latencia haciendo un uso más eficaz de la red. El siguiente fragmento de código muestra un ejemplo que recupera los detalles de dos clientes simultáneamente. El código envía dos solicitudes y, a continuación, realiza otro procesamiento (no mostrado) antes de esperar a recibir los resultados. El Wait método del objeto de caché es similar al método de .NET Framework Task.Wait :

ConnectionMultiplexer redisHostConnection = ...;
IDatabase cache = redisHostConnection.GetDatabase();
...
var task1 = cache.StringGetAsync("customer:1");
var task2 = cache.StringGetAsync("customer:2");
...
var customer1 = cache.Wait(task1);
var customer2 = cache.Wait(task2);

Para más información sobre cómo escribir aplicaciones cliente que pueden usar Azure Cache for Redis, consulte la documentación de Azure Cache for Redis. También hay más información disponible en StackExchange.Redis.

La página Pipelines and multiplexers (Canalizaciones y multiplexores ) del mismo sitio web proporciona más información sobre las operaciones asincrónicas y la canalización con Redis y la biblioteca StackExchange.

Uso del almacenamiento en caché de Redis

El uso más sencillo de Redis para los problemas de almacenamiento en caché es los pares clave-valor en los que el valor es una cadena nointerpreta de longitud arbitraria que puede contener cualquier dato binario. (Básicamente, es una matriz de bytes que se puede tratar como una cadena). Este escenario se ilustra en la sección Implementación de aplicaciones cliente de Redis Cache anteriormente en este artículo.

Tenga en cuenta que las claves también contienen datos no interpretados, por lo que puede usar cualquier información binaria como clave. Sin embargo, cuanto más larga sea la clave, más espacio tardará en almacenarse y más tiempo tardará en realizar operaciones de búsqueda. Para facilitar la facilidad de uso y el mantenimiento, diseñe cuidadosamente el espacio de claves y use claves significativas (pero no detalladas).

Por ejemplo, use claves estructuradas como "customer:100" para representar la clave para el cliente con el identificador 100 en lugar de simplemente "100". Este esquema permite distinguir fácilmente entre valores que almacenan diferentes tipos de datos. Por ejemplo, también puede usar la clave "orders:100" para representar la clave del pedido con el identificador 100.

Además de las cadenas binarias unidimensionales, un valor de un par clave-valor de Redis también puede contener información más estructurada, incluidas listas, conjuntos (ordenados y sin ordenar) y hashes. Redis proporciona un conjunto de comandos completo que puede manipular estos tipos y muchos de estos comandos están disponibles para las aplicaciones de .NET Framework a través de una biblioteca cliente como StackExchange. En la página Introducción a los tipos de datos y abstracciones de Redis en el sitio web de Redis se proporciona información general más detallada sobre estos tipos y los comandos que puede usar para manipularlos.

En esta sección se resumen algunos casos de uso comunes para estos tipos de datos y comandos.

Realización de operaciones atómicas y por lotes

Redis admite una serie de operaciones atómicas get-and-set en valores de cadena. Estas operaciones eliminan los posibles peligros de carrera que pueden producirse al usar comandos y GET independientesSET. Las operaciones disponibles incluyen:

  • INCR, INCRBY, DECRy DECRBY, que realizan operaciones atómicas de incremento y decremento en valores de datos numéricos enteros. La biblioteca StackExchange proporciona versiones sobrecargadas de los IDatabase.StringIncrementAsync métodos y IDatabase.StringDecrementAsync para realizar estas operaciones y devolver el valor resultante que se almacena en la memoria caché. En el fragmento de código siguiente se muestra cómo usar estos métodos:

    ConnectionMultiplexer redisHostConnection = ...;
    IDatabase cache = redisHostConnection.GetDatabase();
    ...
    await cache.StringSetAsync("data:counter", 99);
    ...
    long oldValue = await cache.StringIncrementAsync("data:counter");
    // Increment by 1 (the default)
    // oldValue should be 100
    
    long newValue = await cache.StringDecrementAsync("data:counter", 50);
    // Decrement by 50
    // newValue should be 50
    
  • GETSET, que recupera el valor asociado a una clave y lo cambia a un nuevo valor. La biblioteca StackExchange hace que esta operación esté disponible a través del IDatabase.StringGetSetAsync método . El fragmento de código siguiente muestra un ejemplo de este método. Este código devuelve el valor actual asociado a la clave "data:counter" del ejemplo anterior. A continuación, restablece el valor de esta clave a cero, todo ello como parte de la misma operación:

    ConnectionMultiplexer redisHostConnection = ...;
    IDatabase cache = redisHostConnection.GetDatabase();
    ...
    string oldValue = await cache.StringGetSetAsync("data:counter", 0);
    
  • MGET y MSET, que puede devolver o cambiar un conjunto de valores de cadena como una sola operación. Los IDatabase.StringGetAsync métodos y IDatabase.StringSetAsync se sobrecargan para admitir esta funcionalidad, como se muestra en el ejemplo siguiente:

    ConnectionMultiplexer redisHostConnection = ...;
    IDatabase cache = redisHostConnection.GetDatabase();
    ...
    // Create a list of key-value pairs
    var keysAndValues =
        new List<KeyValuePair<RedisKey, RedisValue>>()
        {
            new KeyValuePair<RedisKey, RedisValue>("data:key1", "value1"),
            new KeyValuePair<RedisKey, RedisValue>("data:key99", "value2"),
            new KeyValuePair<RedisKey, RedisValue>("data:key322", "value3")
        };
    
    // Store the list of key-value pairs in the cache
    cache.StringSet(keysAndValues.ToArray());
    ...
    // Find all values that match a list of keys
    RedisKey[] keys = { "data:key1", "data:key99", "data:key322"};
    // values should contain { "value1", "value2", "value3" }
    RedisValue[] values = cache.StringGet(keys);
    
    

También puede combinar varias operaciones en una sola transacción de Redis, tal como se describe en la sección Transacciones y lotes de Redis que se ha descrito anteriormente en este artículo. La biblioteca StackExchange proporciona compatibilidad con transacciones a través de la ITransaction interfaz .

Se crea un ITransaction objeto mediante el IDatabase.CreateTransaction método . Se invocan comandos a la transacción mediante los métodos proporcionados por el ITransaction objeto .

La ITransaction interfaz proporciona acceso a un conjunto de métodos similares a los a los a los que accede la IDatabase interfaz, excepto que todos los métodos son asincrónicos. Esto significa que solo se realizan cuando se invoca el ITransaction.Execute método . El valor devuelto por el ITransaction.Execute método indica si la transacción se creó correctamente (true) o si se produjo un error (false).

El siguiente fragmento de código muestra un ejemplo que incrementa y disminuye dos contadores como parte de la misma transacción:

ConnectionMultiplexer redisHostConnection = ...;
IDatabase cache = redisHostConnection.GetDatabase();
...
ITransaction transaction = cache.CreateTransaction();
var tx1 = transaction.StringIncrementAsync("data:counter1");
var tx2 = transaction.StringDecrementAsync("data:counter2");
bool result = transaction.Execute();
Console.WriteLine("Transaction {0}", result ? "succeeded" : "failed");
Console.WriteLine("Result of increment: {0}", tx1.Result);
Console.WriteLine("Result of decrement: {0}", tx2.Result);

Recuerde que las transacciones de Redis son a diferencia de las transacciones en bases de datos relacionales. El Execute método simplemente pone en cola todos los comandos que componen la transacción que se va a ejecutar y, si alguno de ellos tiene un formato incorrecto, la transacción se detiene. Si todos los comandos se han puesto en cola correctamente, cada comando se ejecuta de forma asincrónica.

Si se produce un error en algún comando, los demás siguen procesando. Si necesita comprobar que un comando se ha completado correctamente, debe capturar los resultados del comando mediante la propiedad Result de la tarea correspondiente, como se muestra en el ejemplo anterior. La lectura de la propiedad Result bloqueará el subproceso que realiza la llamada hasta que se haya completado la tarea.

Para obtener más información, consulte Transacciones en Redis.

Al realizar operaciones por lotes, puede usar la IBatch interfaz de la biblioteca StackExchange. Esta interfaz proporciona acceso a un conjunto de métodos similares a los a los a los que accede la IDatabase interfaz, excepto que todos los métodos son asincrónicos.

Cree un IBatch objeto mediante el IDatabase.CreateBatch método y, a continuación, ejecute el lote mediante el IBatch.Execute método , como se muestra en el ejemplo siguiente. Este código simplemente establece un valor de cadena, incrementa y disminuye los mismos contadores usados en el ejemplo anterior y muestra los resultados:

ConnectionMultiplexer redisHostConnection = ...;
IDatabase cache = redisHostConnection.GetDatabase();
...
IBatch batch = cache.CreateBatch();
batch.StringSetAsync("data:key1", 11);
var t1 = batch.StringIncrementAsync("data:counter1");
var t2 = batch.StringDecrementAsync("data:counter2");
batch.Execute();
Console.WriteLine("{0}", t1.Result);
Console.WriteLine("{0}", t2.Result);

Es importante comprender que, a diferencia de una transacción, si se produce un error en un comando de un lote porque tiene un formato incorrecto, los demás comandos podrían seguir ejecutándolo. El IBatch.Execute método no devuelve ninguna indicación de éxito o error.

Realizar operaciones de desencadenación y olvidar caché

Redis admite operaciones de desencadenación y olvido mediante marcas de comandos. En esta situación, el cliente simplemente inicia una operación, pero no tiene interés en el resultado y no espera a que se complete el comando. En el ejemplo siguiente se muestra cómo realizar el comando INCR como una operación fire and forget:

ConnectionMultiplexer redisHostConnection = ...;
IDatabase cache = redisHostConnection.GetDatabase();
...
await cache.StringSetAsync("data:key1", 99);
...
cache.StringIncrement("data:key1", flags: CommandFlags.FireAndForget);

Especificar claves que expiran automáticamente

Al almacenar un elemento en una caché de Redis, puede especificar un tiempo de espera después del cual el elemento se quitará automáticamente de la memoria caché. También puede consultar cuánto más tiempo tiene una clave antes de que expire mediante el TTL comando . Este comando está disponible para las aplicaciones stackExchange mediante el IDatabase.KeyTimeToLive método .

El siguiente fragmento de código muestra cómo establecer un tiempo de expiración de 20 segundos en una clave y consultar la duración restante de la clave:

ConnectionMultiplexer redisHostConnection = ...;
IDatabase cache = redisHostConnection.GetDatabase();
...
// Add a key with an expiration time of 20 seconds
await cache.StringSetAsync("data:key1", 99, TimeSpan.FromSeconds(20));
...
// Query how much time a key has left to live
// If the key has already expired, the KeyTimeToLive function returns a null
TimeSpan? expiry = cache.KeyTimeToLive("data:key1");

También puede establecer la hora de expiración en una fecha y hora específicas mediante el comando EXPIRE, que está disponible en la biblioteca StackExchange como método KeyExpireAsync :

ConnectionMultiplexer redisHostConnection = ...;
IDatabase cache = redisHostConnection.GetDatabase();
...
// Add a key with an expiration date of midnight on 1st January 2015
await cache.StringSetAsync("data:key1", 99);
await cache.KeyExpireAsync("data:key1",
    new DateTime(2015, 1, 1, 0, 0, 0, DateTimeKind.Utc));
...

Sugerencia

Puede quitar manualmente un elemento de la memoria caché mediante el comando DEL, que está disponible a través de la biblioteca StackExchange como método IDatabase.KeyDeleteAsync .

Uso de etiquetas para poner en correlación cruzada los elementos almacenados en caché

Un conjunto de Redis es una colección de varios elementos que comparten una sola clave. Puede crear un conjunto mediante el comando SADD. Puede recuperar los elementos de un conjunto mediante el comando SMEMBERS. La biblioteca StackExchange implementa el comando SADD con el IDatabase.SetAddAsync método y el comando SMEMBERS con el IDatabase.SetMembersAsync método .

También puede combinar conjuntos existentes para crear nuevos conjuntos mediante los comandos SDIFF (establecer diferencia), SINTER (intersección de conjunto) y SUNION (unión de conjuntos). La biblioteca StackExchange unifica estas operaciones en el IDatabase.SetCombineAsync método . El primer parámetro de este método especifica la operación set que se va a realizar.

Los fragmentos de código siguientes muestran cómo los conjuntos pueden ser útiles para almacenar y recuperar rápidamente colecciones de elementos relacionados. Este código usa el BlogPost tipo descrito en la sección Implementación de aplicaciones cliente de Redis Cache anteriormente en este artículo.

Un BlogPost objeto contiene cuatro campos: un identificador, un título, una puntuación de clasificación y una colección de etiquetas. El primer fragmento de código siguiente muestra los datos de ejemplo que se usan para rellenar una lista de objetos de BlogPost C#:

List<string[]> tags = new List<string[]>
{
    new[] { "iot","csharp" },
    new[] { "iot","azure","csharp" },
    new[] { "csharp","git","big data" },
    new[] { "iot","git","database" },
    new[] { "database","git" },
    new[] { "csharp","database" },
    new[] { "iot" },
    new[] { "iot","database","git" },
    new[] { "azure","database","big data","git","csharp" },
    new[] { "azure" }
};

List<BlogPost> posts = new List<BlogPost>();
int blogKey = 0;
int numberOfPosts = 20;
Random random = new Random();
for (int i = 0; i < numberOfPosts; i++)
{
    blogKey++;
    posts.Add(new BlogPost(
        blogKey,                  // Blog post ID
        string.Format(CultureInfo.InvariantCulture, "Blog Post #{0}",
            blogKey),             // Blog post title
        random.Next(100, 10000),  // Ranking score
        tags[i % tags.Count]));   // Tags--assigned from a collection
                                  // in the tags list
}

Puede almacenar las etiquetas de cada BlogPost objeto como un conjunto en una caché de Redis y asociar cada conjunto con el identificador de BlogPost. Esto permite a una aplicación encontrar rápidamente todas las etiquetas que pertenecen a una entrada de blog específica. Para habilitar la búsqueda en la dirección opuesta y buscar todas las entradas de blog que comparten una etiqueta específica, puede crear otro conjunto que contenga las entradas de blog que hacen referencia al identificador de etiqueta en la clave:

ConnectionMultiplexer redisHostConnection = ...;
IDatabase cache = redisHostConnection.GetDatabase();
...
// Tags are easily represented as Redis Sets
foreach (BlogPost post in posts)
{
    string redisKey = string.Format(CultureInfo.InvariantCulture,
        "blog:posts:{0}:tags", post.Id);
    // Add tags to the blog post in Redis
    await cache.SetAddAsync(
        redisKey, post.Tags.Select(s => (RedisValue)s).ToArray());

    // Now do the inverse so we can figure out which blog posts have a given tag
    foreach (var tag in post.Tags)
    {
        await cache.SetAddAsync(string.Format(CultureInfo.InvariantCulture,
            "tag:{0}:blog:posts", tag), post.Id);
    }
}

Estas estructuras permiten realizar muchas consultas comunes de forma muy eficaz. Por ejemplo, puede encontrar y mostrar todas las etiquetas de la entrada de blog 1 de esta manera:

// Show the tags for blog post #1
foreach (var value in await cache.SetMembersAsync("blog:posts:1:tags"))
{
    Console.WriteLine(value);
}

Puede encontrar todas las etiquetas que son comunes a la entrada de blog 1 y la entrada de blog 2 mediante la realización de una operación de intersección de conjuntos, como se indica a continuación:

// Show the tags in common for blog posts #1 and #2
foreach (var value in await cache.SetCombineAsync(SetOperation.Intersect, new RedisKey[]
    { "blog:posts:1:tags", "blog:posts:2:tags" }))
{
    Console.WriteLine(value);
}

Y puede encontrar todas las entradas de blog que contienen una etiqueta específica:

// Show the ids of the blog posts that have the tag "iot".
foreach (var value in await cache.SetMembersAsync("tag:iot:blog:posts"))
{
    Console.WriteLine(value);
}

Buscar elementos a los que se ha accedido recientemente

Una tarea común necesaria para muchas aplicaciones es encontrar los elementos a los que se ha accedido más recientemente. Por ejemplo, un sitio de registro podría querer mostrar información sobre las entradas de blog de lectura más recientes.

Puede implementar esta funcionalidad mediante una lista de Redis. Una lista de Redis contiene varios elementos que comparten la misma clave. La lista actúa como una cola de doble finalización. Puede insertar elementos en cualquier extremo de la lista mediante los comandos LPUSH (inserción izquierda) y RPUSH (inserción derecha). Puede recuperar elementos de cualquier extremo de la lista mediante los comandos LPOP y RPOP. También puede devolver un conjunto de elementos mediante los comandos LRANGE y RRANGE.

Los fragmentos de código siguientes muestran cómo puede realizar estas operaciones mediante la biblioteca StackExchange. Este código usa el BlogPost tipo de los ejemplos anteriores. Como un usuario lee una entrada de blog, el IDatabase.ListLeftPushAsync método inserta el título de la entrada de blog en una lista asociada a la clave "blog:recent_posts" en la caché de Redis.

ConnectionMultiplexer redisHostConnection = ...;
IDatabase cache = redisHostConnection.GetDatabase();
...
string redisKey = "blog:recent_posts";
BlogPost blogPost = ...; // Reference to the blog post that has just been read
await cache.ListLeftPushAsync(
    redisKey, blogPost.Title); // Push the blog post onto the list

A medida que se leen más entradas de blog, sus títulos se insertan en la misma lista. La lista se ordena por la secuencia en la que se han agregado los títulos. Las entradas de blog de lectura más recientes están hacia el final izquierdo de la lista. (Si la misma entrada de blog se lee más de una vez, tendrá varias entradas en la lista).

Puede mostrar los títulos de las publicaciones de lectura más recientes mediante el IDatabase.ListRange método . Este método toma la clave que contiene la lista, un punto de partida y un punto final. El código siguiente recupera los títulos de las 10 entradas de blog (elementos de 0 a 9) en el extremo izquierdo de la lista:

// Show latest ten posts
foreach (string postTitle in await cache.ListRangeAsync(redisKey, 0, 9))
{
    Console.WriteLine(postTitle);
}

Tenga en cuenta que el ListRangeAsync método no quita elementos de la lista. Para ello, puede usar los IDatabase.ListLeftPopAsync métodos y IDatabase.ListRightPopAsync .

Para evitar que la lista crezca indefinidamente, puede seleccionar periódicamente los elementos recortando la lista. El fragmento de código siguiente muestra cómo quitar todos los elementos excepto los cinco elementos de la lista más a la izquierda:

await cache.ListTrimAsync(redisKey, 0, 5);

Implementación de una tabla de clasificación

De forma predeterminada, los elementos de un conjunto no se mantienen en ningún orden específico. Puede crear un conjunto ordenado mediante el comando ZADD (el IDatabase.SortedSetAdd método de la biblioteca StackExchange). Los elementos se ordenan mediante un valor numérico denominado puntuación, que se proporciona como parámetro para el comando.

El siguiente fragmento de código agrega el título de una entrada de blog a una lista ordenada. En este ejemplo, cada entrada de blog también tiene un campo de puntuación que contiene la clasificación de la entrada de blog.

ConnectionMultiplexer redisHostConnection = ...;
IDatabase cache = redisHostConnection.GetDatabase();
...
string redisKey = "blog:post_rankings";
BlogPost blogPost = ...; // Reference to a blog post that has just been rated
await cache.SortedSetAddAsync(redisKey, blogPost.Title, blogPost.Score);

Puede recuperar los títulos y las puntuaciones de la entrada de blog en orden ascendente mediante el IDatabase.SortedSetRangeByRankWithScores método :

foreach (var post in await cache.SortedSetRangeByRankWithScoresAsync(redisKey))
{
    Console.WriteLine(post);
}

Nota:

La biblioteca StackExchange también proporciona el IDatabase.SortedSetRangeByRankAsync método , que devuelve los datos en orden de puntuación, pero no devuelve las puntuaciones.

También puede recuperar elementos en orden descendente de puntuaciones y limitar el número de elementos que se devuelven proporcionando parámetros adicionales al IDatabase.SortedSetRangeByRankWithScoresAsync método . En el ejemplo siguiente se muestran los títulos y las puntuaciones de las 10 entradas de blog más clasificadas:

foreach (var post in await cache.SortedSetRangeByRankWithScoresAsync(
                               redisKey, 0, 9, Order.Descending))
{
    Console.WriteLine(post);
}

En el ejemplo siguiente se usa el IDatabase.SortedSetRangeByScoreWithScoresAsync método , que se puede usar para limitar los elementos que se devuelven a los que se encuentran dentro de un intervalo de puntuación determinado:

// Blog posts with scores between 5000 and 100000
foreach (var post in await cache.SortedSetRangeByScoreWithScoresAsync(
                               redisKey, 5000, 100000))
{
    Console.WriteLine(post);
}

Mensaje mediante canales

Además de actuar como una caché de datos, un servidor de Redis proporciona mensajería a través de un mecanismo de publicador o suscriptor de alto rendimiento. Las aplicaciones cliente pueden suscribirse a un canal y otras aplicaciones o servicios pueden publicar mensajes en el canal. Las aplicaciones de suscripción recibirán estos mensajes y podrán procesarlos.

Redis proporciona el comando SUBSCRIBE para que las aplicaciones cliente usen para suscribirse a canales. Este comando espera el nombre de uno o varios canales en los que la aplicación aceptará mensajes. La biblioteca StackExchange incluye la ISubscription interfaz , que permite que una aplicación de .NET Framework se suscriba y publique en canales.

Se crea un ISubscription objeto mediante el GetSubscriber método de la conexión al servidor de Redis. A continuación, escuchará mensajes en un canal mediante el SubscribeAsync método de este objeto. En el ejemplo de código siguiente se muestra cómo suscribirse a un canal denominado "messages:blogPosts":

ConnectionMultiplexer redisHostConnection = ...;
ISubscriber subscriber = redisHostConnection.GetSubscriber();
...
await subscriber.SubscribeAsync("messages:blogPosts", (channel, message) => Console.WriteLine("Title is: {0}", message));

El primer parámetro del Subscribe método es el nombre del canal. Este nombre sigue las mismas convenciones que usan las claves en la memoria caché. El nombre puede contener cualquier dato binario, pero se recomienda usar cadenas relativamente cortas y significativas para ayudar a garantizar un buen rendimiento y mantenimiento.

Tenga en cuenta también que el espacio de nombres utilizado por los canales es independiente del usado por las claves. Esto significa que puede tener canales y claves que tengan el mismo nombre, aunque esto puede dificultar el mantenimiento del código de la aplicación.

El segundo parámetro es un delegado de acción. Este delegado se ejecuta de forma asincrónica cada vez que aparece un nuevo mensaje en el canal. Este ejemplo simplemente muestra el mensaje en la consola (el mensaje contendrá el título de una entrada de blog).

Para publicar en un canal, una aplicación puede usar el comando PUBLICAR de Redis. La biblioteca StackExchange proporciona el IServer.PublishAsync método para realizar esta operación. El siguiente fragmento de código muestra cómo publicar un mensaje en el canal "messages:blogPosts":

ConnectionMultiplexer redisHostConnection = ...;
ISubscriber subscriber = redisHostConnection.GetSubscriber();
...
BlogPost blogPost = ...;
subscriber.PublishAsync("messages:blogPosts", blogPost.Title);

Hay varios puntos que debe comprender sobre el mecanismo de publicación o suscripción:

  • Varios suscriptores pueden suscribirse al mismo canal y todos recibirán los mensajes publicados en ese canal.
  • Los suscriptores solo reciben mensajes que se han publicado después de suscribirse. Los canales no están almacenados en búfer y, una vez publicado un mensaje, la infraestructura de Redis inserta el mensaje en cada suscriptor y, a continuación, lo quita.
  • De forma predeterminada, los suscriptores reciben mensajes en el orden en que se envían. En un sistema muy activo con un gran número de mensajes y muchos suscriptores y publicadores, la entrega secuencial garantizada de mensajes puede ralentizar el rendimiento del sistema. Si cada mensaje es independiente y el orden no es importante, puede habilitar el procesamiento simultáneo por parte del sistema Redis, lo que puede ayudar a mejorar la capacidad de respuesta. Puede lograrlo en un cliente de StackExchange estableciendo PreserveAsyncOrder de la conexión usada por el suscriptor en false:
ConnectionMultiplexer redisHostConnection = ...;
redisHostConnection.PreserveAsyncOrder = false;
ISubscriber subscriber = redisHostConnection.GetSubscriber();

Consideraciones sobre la serialización

Al elegir un formato de serialización, considere el equilibrio entre rendimiento, interoperabilidad, control de versiones, compatibilidad con sistemas existentes, compresión de datos y sobrecarga de memoria. Al evaluar el rendimiento, recuerde que las pruebas comparativas dependen mucho del contexto. Es posible que no reflejen la carga de trabajo real y que no tengan en cuenta las bibliotecas o versiones más recientes. No hay ningún serializador "más rápido" único para todos los escenarios.

Algunas opciones a considerar incluyen:

  • Los búferes de protocolo (también denominados protobuf) son un formato de serialización desarrollado por Google para serializar los datos estructurados de forma eficaz. Usa archivos de definición fuertemente tipados para definir estructuras de mensajes. A continuación, estos archivos de definición se compilan en código específico del lenguaje para serializar y deserializar mensajes. Protobuf se puede usar a través de mecanismos RPC existentes o puede generar un servicio RPC.

  • Apache Thrift usa un enfoque similar, con archivos de definición fuertemente tipados y un paso de compilación para generar el código de serialización y los servicios RPC.

  • Apache Avro proporciona una funcionalidad similar a los búferes de protocolo y Thrift, pero no hay ningún paso de compilación. En su lugar, los datos serializados siempre incluyen un esquema que describe la estructura.

  • JSON es un estándar abierto que usa campos de texto legibles para personas. Tiene una amplia compatibilidad multiplataforma. JSON no usa esquemas de mensajes. Ser un formato basado en texto, no es muy eficaz a través de la conexión. Sin embargo, en algunos casos, es posible que devuelva elementos almacenados en caché directamente a un cliente a través de HTTP, en cuyo caso almacenar JSON podría ahorrar el costo de deserializar desde otro formato y, a continuación, serializar en JSON.

  • binary JSON (BSON) es un formato de serialización binaria que usa una estructura similar a JSON. BSON se diseñó para ser ligero, fácil de examinar y rápido para serializar y deserializar, en relación con JSON. Las cargas son comparables en tamaño a JSON. En función de los datos, una carga de BSON puede ser menor o mayor que una carga JSON. BSON tiene algunos tipos de datos adicionales que no están disponibles en JSON, en particular BinData (para matrices de bytes) y Date.

  • MessagePack es un formato de serialización binaria diseñado para ser compacto para la transmisión por cable. No hay esquemas de mensaje ni comprobación de tipos de mensaje.

  • Bond es un marco multiplataforma para trabajar con datos esquematizados. Admite la serialización y deserialización entre lenguajes. Las diferencias importantes de otros sistemas enumerados aquí son la compatibilidad con la herencia, los alias de tipo y los genéricos.

  • gRPC es un sistema RPC de código abierto desarrollado por Google. De forma predeterminada, usa Búferes de protocolo como lenguaje de definición y formato de intercambio de mensajes subyacente.

Pasos siguientes

Los siguientes patrones también pueden ser relevantes para su escenario al implementar el almacenamiento en caché en las aplicaciones:

  • Patrón cache-aside: este patrón describe cómo cargar datos a petición en una memoria caché desde un almacén de datos. Este patrón también ayuda a mantener la coherencia entre los datos que se mantienen en la memoria caché y los datos del almacén de datos original.

  • El patrón de particionamiento proporciona información sobre la implementación de particiones horizontales para ayudar a mejorar la escalabilidad al almacenar y acceder a grandes volúmenes de datos.