Almacenamiento en caché por diccionarios en memoria. ¿Lo estamos haciendo todo mal?

8

Este enfoque es más o menos la forma aceptada de hacer cualquier cosa en nuestra empresa. Un ejemplo simple: cuando se solicita un dato para un cliente de un servicio, buscamos todos los datos para ese cliente (parte relevante para el servicio) y lo guardamos en un diccionario en memoria y luego lo servimos desde allí en las siguientes solicitudes (ejecutamos servicios singleton). Cualquier actualización va a DB, luego actualiza el diccionario en memoria. Parece todo simple e inofensivo, pero a medida que implementamos reglas comerciales más complicadas, el caché se desincroniza y tenemos que lidiar con errores difíciles de encontrar. A veces aplazamos la escritura en la base de datos, manteniendo nuevos datos en caché hasta entonces. Hay casos en los que almacenamos millones de filas en la memoria porque la tabla tiene muchas relaciones con otras tablas y necesitamos mostrar datos agregados rápidamente.

Todo este manejo de caché es una gran parte de nuestra base de código y siento que esta no es la forma correcta de hacerlo. Todo este malabarismo agrega demasiado ruido al código y dificulta la comprensión de la lógica comercial real. Sin embargo, no creo que podamos servir datos en un período de tiempo razonable si tenemos que acceder a la base de datos cada vez.

No estoy contento con la situación actual, pero no tengo una mejor alternativa. Mi única solución sería usar el caché NHibernate de segundo nivel, pero casi no tengo experiencia con él. Sé que muchas campañas usan Redis o MemCached en gran medida para obtener rendimiento, pero no tengo idea de cómo las integraría en nuestro sistema. Tampoco sé si pueden funcionar mejor que las estructuras y consultas de datos en memoria.

¿Hay algún enfoque alternativo que deba considerar?

usuario73983
fuente

Respuestas:

9

Primero tu última pregunta: ¿Por qué Redis / memcached?

No, no son (generalmente) más rápidos que simples diccionarios en proceso. La ventaja viene cuando tiene varios procesos de trabajo, o incluso muchas máquinas de capa de aplicación. En ese caso, en lugar de que cada proceso tenga su propio caché pequeño, todos comparten un único caché grande (distribuido). Con cachés más grandes, obtienes mejores proporciones de aciertos.

Como puede ver, la capa de caché se convierte en un recurso compartido, al igual que la base de datos, pero (con suerte) más rápido.

Ahora, sobre la gran parte: ¿cómo evitar el desorden?

Parece que su problema es mantener la caché coherente y al mismo tiempo desacoplarla de la base de datos. Veo tres puntos de dolor allí:

  1. invalidación de caché. Esto es dificil. A veces, la solución más fácil es agregar una ID de generación a cada registro y usarla como parte de la clave de caché. Cuando se actualizan los datos, obtienes un ID de nueva generación, y la siguiente consulta de caché no tendrá éxito, así que vas a la base de datos y actualizas el caché. Por supuesto, la entrada (ahora no utilizada) debe tener un tiempo de expiración razonable para que finalmente se elimine del caché.

  2. respóndeme. Dices que trabajas en el caché y actualizas la base de datos más tarde. Esto es peligroso; La mayoría de las arquitecturas evitan esa idea. Un paso en la dirección correcta sería marcar cada entrada nueva o modificada en el caché como 'sucia' para que pueda ser vaciada a la base de datos por un proceso desacoplado. Una mejor idea podría ser agregar a una cola de mensajes tan pronto como se modifique, haciendo que la escritura en la base de datos sea 'en línea pero asíncrona'. Al final, creo que debería darse cuenta de que este no es un uso válido para una memoria caché, se trata de un "área de ensayo" que debe tratarse con una arquitectura diferente a una capa de memoria caché.

  3. sincronización entre procesos: dado que su caché en proceso es privada para cada proceso, cualquier modificación allí no se propaga a otros procesos hasta que se vacíen a la base de datos. Esto podría ser correcto en el diseño de su aplicación (tipo de aislamiento de transacciones de los pobres), pero podría tener resultados no deseados. Una arquitectura mucho más manejable es una capa de caché que es solo una API más rápida para la base de datos, con las mismas propiedades compartidas que la base de datos y tan 'autorizada' como ella. Para eso necesita cachés fuera de proceso, como memcached o Redis.

Javier
fuente
8
Solo hay dos cosas difíciles en informática: invalidación de caché y nombrar cosas.
Michael Borgwardt
12
Solo hay dos cosas difíciles en Ciencias de la Computación: invalidación de caché, nombrar cosas y errores fuera de uno.
Matthew King el
2
@MatthewKing Solo hay 3 cosas difíciles en informática: errores de dos en dos.
Jimmy Hoffa
@MatthewKing, me encanta el humor. :)
Anthony Gatlin el