2009-09-17 35 views
5

Soy nuevo en memcached. ¿Este código es vulnerable a la condición de carrera de caché expirada? ¿Cómo lo mejorarías?memcacheD Esto está bien?

$memcache = new Memcache; 
$memcache->connect('127.0.0.1'); 
$arts = ($memcache===FALSE) ? FALSE : $memcache->get($qparams); 
if($arts===FALSE) { 
    $arts=fetchdb($q, $qparams); 
    $memcache->add($qparams, $arts, MEMCACHE_COMPRESSED, 60*60*24*3); 
} 
if($arts<>FALSE) { 
    // do stuff 
} else { 
    // empty dataset 
} 
  • qparams $ contiene los parámetros de la consulta, por lo que lo estoy usando como clave.
  • $ arts get es una matriz con todos los campos que necesito para cada elemento.

Digamos que la consulta X tiene 100 filas. Un poco después de que la fila # 50 es modificada por otro proceso (digamos que el precio minorista se incrementa).

  • ¿Qué debo hacer con respecto al caché?
  • ¿Cómo puedo saber en la fila n. ° 50 está en la memoria caché?
  • ¿Debería invalidar TODAS las entradas en la memoria caché? (Suena demasiado para mí).

Respuesta

3

¿Este código es vulnerable a la condición de carrera de caché expirada? ¿Cómo lo mejorarías?

. Si dos (o más) clientes simultáneos intentan obtener la misma clave de la memoria caché y terminan extrayéndola de la base de datos. Tendrá picos en la base de datos y por períodos de tiempo la base de datos estará bajo mucha carga. Esto se llama estampida de caché. Hay un par de formas de manejar esto:

  • Para los elementos nuevos, precaliente la caché (básicamente significa que carga previamente los objetos que necesita antes de que el sitio se active).
  • Para los artículos que caducan periódicamente crean un tiempo de expiración que es un poco futuro que el tiempo de vencimiento real (digamos 5-10 minutos). Luego, cuando extrae el objeto del caché, verifique si el tiempo de caducidad está cerca, el caché en el futuro para evitar que otro cliente actualice el caché y actualice desde la base de datos. Para que esto funcione sin estampillas de caché, necesitará implementar bloqueo de clave o usar tokens de cas (requeriría la última biblioteca de cliente para funcionar).

Para obtener más información, consulte memcached faq.

Digamos que la consulta X tiene 100 filas. Un poco después de que la fila # 50 es modificada por otro proceso (digamos que el precio minorista se incrementa).

tiene tres tipos de datos en caché:

  1. objetos
  2. listas de objetos
  3. datos generados

Lo que suelo hacer es mantener los objetos como separados teclas y luego usar "punteros" de caché en las listas. En su caso tiene N objetos en algún lugar de la memoria caché (digamos que las claves son 1,2..N), y luego tiene su lista de objetos en una matriz array(1,2,3,10,42...). Cuando decide cargar la lista con objetos, carga la clave de lista desde la memoria caché, luego carga los objetos reales desde la memoria caché (usando getMulti para reducir las solicitudes). En este caso, si alguno de los objetos se actualiza, usted lo actualiza en un solo lugar y se actualiza automáticamente en todas partes (sin mencionar que se ahorra una gran cantidad de espacio con esta técnica).

Editar: decidió añadir un poco más de información sobre el tiempo de caducidad de búsqueda hacia delante .

Configura su objeto con un dato de caducidad x y lo guarda en la base de datos con una fecha de caducidad de x+5minutes. Estos son los pasos que se dan cuando se carga el objeto de la caché:

  1. Comprobar si es el momento para actualizar (time() - x < 0)
  2. Si es así, cerrar la llave para que nadie pueda actualizar la misma mientras está actualizando la ít. Si no puede bloquear la tecla, alguien más ya está actualizando la clave, y se convierte en un SEP (Somebody Else's Problem). Como memcached no tiene solución para los bloqueos, debes diseñar tu propio mecanismo. Normalmente hago esto agregando una clave separada con el valor de las claves originales + ":lock" al final. Usted debe configurar esta clave para caducar en la cantidad más corta posible (para memcached que es de 1 segundo).
  3. Si obtuvo un candado en la llave, primero guarde el objeto con un nuevo tiempo de vencimiento (de esta manera usted está seguro de que ningún otro cliente intentará bloquear la llave), luego continúe con su negocio y actualice la llave del base de datos y guarde el nuevo valor nuevamente con los vencimientos de anticipación apropiados (vea el punto 1).

Espero que esto aclare todo para arriba :)

+0

Por el momento expirará en el futuro te. No lo entiendo Digamos que mi tiempo de "cierre de caducidad" es de 5 minutos, y espero que el ítem # 5432 expire a las 17: 00hs. A las 16: 55hs, cuatro usuarios diferentes solicitan el artículo # 5432. Todos alcanzarán el db ... Buscaré tokens de bloqueo y cas, pero no entiendo el beneficio de hacer esto x minutos antes. –

+0

Si lo hace 5 minutos antes de que caduque el objeto, el resto de los clientes aún puede usar el objeto obsoleto mientras carga el nuevo de la base de datos. Concédale que todavía tiene que usar tokens de bloqueo y cas para evitar que todos se actualicen de inmediato. Podría hacer una suposición probabilística y actualizar con una mayor probabilidad cuanto más cerca esté el tiempo de expiración que obtenga. De cualquier forma, desea tener solo un cliente que acceda a la base de datos. Incluso podría configurar un cron que lo haría para usted en segundo plano, pero exigiría un objeto de aspecto muy específico :) –

+0

He editado la respuesta con más detalles. Espero que esté más claro ahora :) –

1

Tiene que invalidar cualquier objeto en caché que contenga un elemento modificado. O bien tiene que modificar el mecanismo de caché para almacenar elementos en un nivel más granular o invalidar la entrada completa.

Es básicamente lo mismo que decir que está almacenando en caché toda la base de datos en una sola entrada de caché. O lo vence o no.

Cuestiones relacionadas