2010-06-22 40 views
18

¿Cómo verifico en PHP si un valor está almacenado en Memcache sin recuperarlo? No me gusta ir a buscarlo porque los valores que he establecido tienen un tamaño de 1MB y después de recuperarlo, no me sirve, así que estoy desperdiciando recursos. Estoy usando esto en una secuencia de comandos que comprueba si ciertas claves se almacenan en caché en Memcache y, de no ser así, las lee desde un origen de datos lento y las establece en Memcache.Comprobar si existe una clave en Memcache

Editar: ¿Qué ocurre si uso Memcached::append para anexar NULL a la clave que estoy revisando? Devuelve TRUE en caso de éxito o FALSE en caso de error. El Memcached::getResultCode devolverá Memcached::RES_NOTSTORED si la clave no existe. De esta forma verifico si la clave existe y debería poner la llave en la parte superior de la lista LRU ¿verdad?

Gracias.

Respuesta

3

Básicamente lo que quieres hacer es llenar memcached con tus datos, ¿verdad?

Lo que sucede es que no es muy útil preguntar si hay una clave sin recuperar el valor. Vea este escenario de caso:

Usted pregunta si existe una clave y lo hace. Justo después de preguntar, los datos de la clave se expulsan del caché. Mientras su respuesta regresa diciendo que la fecha está allí, la realidad es que los datos no están allí. Entonces perdiste el tiempo preguntando, porque la respuesta es diferente de la realidad.

Supongo que lo que quiere hacer es preguntar si existe una clave, y luego, si no, llenarla con sus datos. ¿Por qué no llenas directamente todos los datos? Por cierto, ¿ha considerado que mientras llena los datos de Memcached, podría estar expulsando las claves que acaba de insertar?

+0

Sí, pero primero debo asegurarme de que no esté configurado. Porque si ya está configurado, entonces desperdiciaría la lectura de los datos de una fuente lenta. Si utilizo get() para verificar, perderé IO de red porque el tamaño es 1MB. – Matic

+0

@Matic el "problema" es que memcached no se diseñó con IO de red en mente, así que supongo que esa es la razón por la que se omitió dicha funcionalidad. Si la red IO es un problema, es mejor utilizar una base de datos kv. Entiendo que a veces simplemente no desea una asignación adicional, incluso si la red IO no es un problema, pero esto no es posible con memcached. Redis tiene una de esas características. – themihai

6

no estoy seguro si esto es de alguna ayuda para usted, pero se puede utilizar

Memcache::add ( string $key , mixed $var) 

volverá falsa si la clave ya existe.

En caso de que se devuelve true puede usar

Memcache::delete ( string $key) 

para eliminar la clave que acaba de establecer. De esta forma, no necesitará buscar los datos.

+0

Y si devuelve cierto, no se olvide de hacer un Memcached :: eliminar después de –

+0

@Serty Oan: ambos escribieron al mismo tiempo :) – Thariama

+0

grandes mentes piensan igual;) –

0

Esto no tiene sentido desde la perspectiva de Memcached. Si desea evitar el origen de datos lento (según un trabajo programado, supongo?), Guarde los datos en un origen de datos más rápido pero estable (por ejemplo, un archivo) en su trabajo programado. Cuando necesite los datos, intente leerlos desde Memcached primero, y si eso falla, lea el archivo y guárdelo en Memcached.

Memcached no puede darle una buena respuesta, ya que pakore ya ha respondido. La arquitectura no pretende ser un origen de datos estable.

3

Lo he resuelto utilizando Memcached :: append. Intento añadir valor NULL y si devuelve TRUE, significa que la clave existe. Si devuelve FALSE, significa que la clave no existe. Si la clave existe, también la colocará en la parte superior de la lista de LRU.

+1

Debe asegurarse de que 'Memcached :: OPT_COMPRESSION' esté configurado en falso, de lo contrario, la operación siempre fallará. Además, puede ser mejor agregar la cadena vacía * '' * en lugar de * NULL * .. –

+0

Para aquellos que usan 'Memcache', no' Memcached': 'append' método está disponible solo en este último. – TheFrost

2

No me gusta la sugerencia de agregar/eliminar porque inserta datos no válidos, su aplicación puede depender de ser válida. Si su aplicación hace esta suposición, causaría un error sutil que ocurre de vez en cuando debido a la condición de carrera que presenta.

Una solución es echar un vistazo al valor y si sus datos temporales pretenden que no están allí, pero que requerirían que todos los códigos de la aplicación usen la misma API o tendrían que actualizar la aplicación, ninguno de los dos es divertido y algo propenso a errores debido a la complejidad adicional.

Si el conjunto de claves es lo suficientemente pequeño, puede almacenarlo en memcached y usarlo para determinar si se recuperan o no los datos de la fuente nuevamente. Sin embargo, si es tan grande o más grande, el valor de este método es peor que solo obtener el valor completo de memcached.

A continuación, podría resolver el nuevo problema mediante el uso de un índice para separar las llaves en conjuntos más pequeños (por supuesto, una buena manera de fragmentar estos datos para cada cubo es un cierto tamaño es decirlo que hacerlo más fácil.)

La implementación consistiría en utilizar anexar o adjuntar memcached para mantener su lista de claves vinculadas a alguna clave maestra o una clave maestra que apunte a un conjunto de sub_keys que apuntan a las teclas mismas :)

De cualquier forma que esté haciendo la aplicación es cada vez más compleja, por lo que solo recomendaría hacer esto si existe un cuello de botella (como lo sería si la existencia de las claves necesita ser revisada frecuentemente a través de una red), o si usabil ity en una preocupación (latencia).

En mi caso, no haré esto porque solo voy a acceder a memcached en localhost y lo estoy usando como una extensión más de mi aplicación para almacenar recursos que tardan más de unos segundos en cargarse normalmente.

0

La manera más fácil es obtener la clave dada y convertirla en un valor booleano.

(bool) Memcache::get(string $key) 
+1

Como dijo OP, él no quiere 'obtener' debido al gran tamaño de los datos. –

1

Simplemente no es posible, no es posible verificar si solo existe la clave o no. mejor que crear una clave separada con los valores verdadero/falso para comprobar la existencia de claves

0

(un poco tarde a la discusión, pero) en realidad puede acceder a todas las llaves/valores almacenados en la Memcache por:

$allSlabs = $memcache->getExtendedStats('slabs'); 
$items = $memcache->getExtendedStats('items'); 

foreach ($allSlabs as $server => $slabs) 
    foreach ($slabs as $slabId => $slabMeta) 
     foreach ($memcache->getExtendedStats('cachedump', (int) $slabId) as $entries) 
      if (!empty($entries)) 
       foreach ($entries as $key => $entry) 
+0

Funcionaría, pero es similar a usar un mazo para romper una nuez, lo más importante al hacer eso cada vez solo para verificar si existe una llave, sería menos oneroso obtener los datos y verificar el código del resultado. –

+0

@SteveChilds: se vuelve útil si no se conoce el valor clave exacto. Por ejemplo, si está almacenando ciertas instancias de objetos en su memcache bajo claves similares (object_type-object_id # object_options) y desea volver a cargar todas las instancias de object_type-object_id. Realmente, todo depende de la configuración y la base de código. – eithed

+0

Ah, vale, sí, en su caso de uso sí tiene sentido, pero en el contexto del PO, es excesivo. –

4

Me pregunto por qué Memcached no tiene un método especial para ello. Esto es lo que vine después de algunas consideraciones:

function has($key) 
{ 
    $m->get($key) 

    return \Memcached::RES_NOTFOUND !== $m->getResultCode(); 
} 
1

memcached ahora tiene el comando cas. se puede usar con una única cas como 0 para obtener el existe o respuestas not_found:

$ telnet localhost 11211 
Trying 127.0.0.1... 
Connected to localhost. 
Escape character is '^]'. 
set hello 0 0 5 
12345 
STORED 
gets hello 
VALUE hello 0 5 6743 
12345 
END 
cas hello 0 0 0 0 

EXISTS 
cas foo 0 0 0 0 

NOT_FOUND 

En la transcripción anterior, lo primero que utiliza el comando set para establecer un hola clave con el valor 12345. A continuación, la recupera, que también devolvió un cas único 6743. Luego intento usar el comando cas para reemplazar el valor con nada, pero como el cas único que utilicé es 0, obtuve el error EXISTS.

Finalmente trato de usar cas para establecer una clave foo que no existe y recuperar el error NOT_FOUND.

Dado que memcached usa un valor de incremento global para cas único, usar 0 es seguro que no es válido para el elemento que está intentando establecer y que si EXISTE presentara un error EXISTS.

Lo sentimos, no estoy familiarizado con el cliente de memcache de php para ponerlo en el código php.

0

Necesitaba una prueba confiable para saber si usar Memcache Set o Memcache replace.

Esto es lo que terminé con.

Otra opción sería configurar un socket de red para la consulta de Memcache, pero al final terminaría haciendo lo mismo y esta conexión ya existe, ahorrándome la sobrecarga de hacer y mantener otra conexión como en Joel Chen's responder.

$key = 'test'; 
$value = 'foobarbaz'; 
/** 
* Intricate dance to test if key exists. 
* If it doesn't exist flags will remain a boolean and we need to use the method set. 
* If it does exist it'll be set to integer indicating the compression and what not, then we need to use replace. 
*/ 
$storageFlag = (is_null($value) || is_bool($value) || is_int($value) || is_float($value) ? false : MEMCACHE_COMPRESSED); 
$flags = false; 
$memcache->get($key, $flags); 
if(false === $flags) { 
    $memcache->set($key, $value, storageFlag , $minutes); 
} 
else { 
    $memcache->replace($key, $value, storageFlag, $minutes); 
} 

Ahora, si tiene "datos grandes" la solución es bastante simple. Usa una segunda clave en cojoin que contenga algo simple como un entero para verificar. Siempre úselos juntos y no tendrá problemas.

$key = 'test'; 
$value = 'foobarbaz'; 

$storageFlag = (is_null($value) || is_bool($value) || is_int($value) || is_float($value) ? false : MEMCACHE_COMPRESSED); 
$flags = false; 
$exist_key = $key.'_exists'; 

$memcache->get($exist_key, $flags); 
if(false === $flags) { 
    $memcache->set($key, $value, storageFlag , $minutes); 
    $memcache->set($exist_key, 42, false , $minutes); 
} 
else { 
    $memcache->replace($key, $value, storageFlag, $minutes); 
    $memcache->replace($exist_key, 42, false , $minutes); 
} 
Cuestiones relacionadas