2008-11-10 23 views
10

Es fácil ajustar el almacenamiento en memoria caché de memcached opcional alrededor de las consultas de bases de datos existentes. Por ejemplo:Patrón de diseño para el almacenamiento en memoria caché de datos memcached

Viejo (DB-only):

function getX 
    x = get from db 
    return x 
end 

Nueva (DB con Memcache):

function getX 
    x = get from memcache 
    if found 
     return x 
    endif 

    x = get from db 
    set x in memcache 
    return x 
end 

La cosa es, sin embargo, eso no es siempre como uno quiere almacenar. Por ejemplo, tomar las dos consultas siguientes:

-- get all items (recordset) 
SELECT * FROM items; 

-- get one item (record) 
SELECT * FROM items WHERE pkid = 42; 

Si tuviera que utilizar lo anterior pseudo-código para manejar el almacenamiento en caché, estaría almacenar todos los campos de objeto 42 dos veces. Una vez en el gran conjunto de registros y una sola vez. Mientras que prefiero hacer algo como esto:

SELECT pkid FROM items; 

y almacenar ese índice de PK en caché. Luego, guarde en caché cada registro individualmente.

Por lo tanto, en resumen, la estrategia de acceso a datos que funcionará mejor para la base de datos no encaja perfectamente con la estrategia de Memcache. Como quiero que la capa de Memcache sea opcional (es decir, si Memcache está inactivo, el sitio aún funciona), quiero tener lo mejor de ambos mundos, pero para hacerlo, estoy bastante seguro de que necesitaré mantener una muchas de las consultas en 2 formas diferentes (1. índice de búsqueda, luego registros, y 2. recuperar conjunto de registros en una consulta). Se vuelve más complicado con la paginación. Con el DB, haría consultas LIMIT/OFFSET SQL, pero con Memcache simplemente obtendría el índice de PK y luego obtendría por lotes el segmento relevante de la matriz.

No estoy seguro de cómo diseñarlo con cuidado, ¿alguien tiene alguna sugerencia?

Mejor aún, si se ha enfrentado a esto usted mismo. ¿Cómo lo manejas?

Respuesta

1

Bueno, supongo que es algo con lo que tendrás que vivir. Memcahced funcionará mejor si no haces cosas en lotes. Por ejemplo, es genial para cosas como "¿dónde están las cosas para este usuario? Aquí hay un montón de cosas para este usuario". Esto realmente no significa que esta consulta no haga lotes. Por supuesto que sí, si algunas de las cosas del usuario son cosas como sus publicaciones.

Supongo que el problema que tendrá es casos donde está mezclando consultas que necesitan para obtener un elemento de la base de datos por sí mismo y algunos que se agrupan en el mismo tipo de los elementos anteriores.

Siempre hay otra cara de la situación. Si realmente quieres ponerte peludo con tu implementación, puedes cambiar tus consultas por lotes para que no incluyan los elementos ya presentes en memcached. Muy muy feo ...

En mi opinión, siempre se reduce a "qué consultas hago yo realmente quiero caché? "

EDIT:

La manera iba a ir sobre esto es:

  • -consulta de un solo artículo - si en memcached, el uso que uno, de lo contrario buscarlo a DB y actualización de memcached.
  • Consulta por lotes: no se preocupe por qué elementos están en memcached, simplemente obtenga todo y actualice memcached.

Esto, por supuesto, asume que las consultas por lotes ya toman mucho más tiempo en completarse y, por lo tanto, ya estoy pasando tanto tiempo que puedo vivir con búsquedas externas de elementos ya en la memoria caché.

Sin embargo, con el tiempo, su caché contendrá muchos de los elementos si utiliza mucho las consultas por lotes. Por lo tanto, deberá alcanzar el equilibrio para determinar en qué punto aún desea realizar las búsquedas en la base de datos. Lo bueno es que si la consulta por lotes es más temprana en el ciclo de vida de sus aplicaciones, todo se almacenará en caché antes. Después de la primera consulta por lotes, puede decirse a sí mismo que ya no necesita buscar en DB a menos que las actualizaciones o eliminaciones invaliden los datos en la memoria caché.

+0

Gracias por su respuesta Cem. Suponiendo que solo estoy almacenando en caché lo que realmente necesito almacenar en caché. ¿Tendría alguna idea de cómo administrar las dos estrategias (DB y DB + Memcache) con la menor duplicación de código? –

3

Lea sobre el Identity Map patrón. Esta es una forma de asegurarse de que solo conserve una copia de una fila determinada en su espacio de aplicación. Ya sea que lo almacene en objetos memcached o simplemente simples, esta es una forma de manejar lo que desea. Supongo que el Mapa de identidad se usa mejor cuando normalmente busca una fila a la vez.

Cuando obtiene subconjuntos completos de una tabla, entonces debe procesar cada fila individualmente. Con frecuencia puede tener el dilema de si está aprovechando al máximo su caché, porque si el 99% de sus filas están en la memoria caché pero una necesita ser extraída de la base de datos, debe ejecutar la consulta SQL de todos modos (al menos una vez).

Puede transformar la consulta SQL para obtener solo las filas que no están en la memoria caché, pero no es trivial realizar esta transformación automáticamente sin hacer que la consulta SQL sea más costosa.

4

Si está utilizando una memoria caché, para aprovecharla al máximo, debe aceptar que sus datos estarán obsoletos hasta cierto punto, y que algunas partes de los datos estarán desincronizados con cada uno otro. Tratar de mantener todos los registros actualizados manteniendo una sola copia es algo que queda mejor para las bases de datos relacionales, así que si este es el comportamiento que necesita, entonces probablemente esté mejor con un potente servidor de bases de datos de 64 bits con mucha RAM para que pueda realizar su propio almacenamiento en caché interno.

Si puede aceptar datos obsoletos (que necesitará si la escalabilidad real es importante), entonces un enfoque es simplemente arrojar todo el conjunto de resultados al caché; no te preocupes por la duplicación. RAM es barato. Si encuentra que su caché se está llenando, simplemente compre más RAM y/o servidores de caché. Por ejemplo, si tiene una consulta que representa los elementos 1-24 en un conjunto filtrado por condiciones X e Y, utilice una clave de caché que contenga toda esta información, y luego cuando se le solicite la misma búsqueda simplemente devuelva el conjunto de resultados completo del cache. O obtiene el conjunto de resultados completo de la memoria caché de un golpe o va a la base de datos.

Lo más difícil es determinar cuántos datos pueden estar obsoletos y qué tan obsoletos pueden ser sin (a) las personas que notan demasiado o (b) rompiendo los requisitos comerciales, como los intervalos mínimos de actualización.

Este enfoque funciona bien para las aplicaciones de lectura mayoritaria, en particular las que tienen consultas paginado y/o un conjunto finito de criterios de filtro para los datos. También significa que su aplicación funciona exactamente igual con el caché activado o desactivado, solo con un 0% de índice de acierto cuando el caché está desactivado. Es el enfoque que tomamos en blinkBox en casi todos los casos.

1

Aquí está mi entendimiento de cómo lo hace NHibernate (y por lo tanto probablemente Hibernate). Tiene 4 cachés:

  • fila de caché: esto almacena en caché filas de bases de datos. La clave de la memoria caché es TableName # id, las otras entradas son los valores de la fila.
  • caché de consultas: esto almacena en caché los resultados devueltos para una consulta en particular. La clave de caché es la consulta con parámetros, los datos son una lista de las claves de fila Id de TableName # que se devolvieron como resultados de consulta.
  • caché de colecciones: almacena en caché los objetos hijo de cualquier padre determinado (que NHibernate permite cargar con pereza). De modo que si accede a myCompany.Employees, la colección de empleados se almacenará en caché en el caché de colecciones. La clave de la memoria caché es CollectionName # entityId, los datos son una lista de las claves de la fila Id del TableName # id para las filas secundarias.
  • caché de actualización de tabla: una lista de cada tabla y cuándo se actualizó por última vez. Si se actualizó una tabla después de almacenar los datos en caché, los datos se consideran obsoletos.

Esta es una solución bastante flexible, es muy eficiente en cuanto a espacio, y garantiza que los datos no estarán obsoletos. La desventaja es que una sola consulta puede requerir varias visitas de ida y vuelta a la memoria caché, lo que puede ser un problema si el servidor de caché está en la red.

Cuestiones relacionadas