2010-06-22 18 views
18

Come faccio a verificare in PHP se un valore è memorizzato in Memcache senza recuperarlo? Non mi piace recuperarlo perché i valori che ho impostato sono tutti di 1 MB di dimensioni e dopo averlo scaricato, non ne ho alcuna utilità, quindi sto sprecando risorse. Lo sto usando in uno script che controlla se alcune chiavi sono memorizzate nella cache di memcache e in caso contrario le legge da una sorgente di dati lenta e le imposta in memcache.Verificare se esiste una chiave in Memcache

Modifica: Cosa succede se uso Memcached::append per aggiungere NULL alla chiave che sto verificando? Restituisce TRUE in caso di successo o FALSE in caso di errore. Memcached::getResultCode restituirà Memcached::RES_NOTSTORED se la chiave non esiste. In questo modo, controllo se la chiave esiste e dovrebbe mettere la chiave sopra l'elenco LRU giusto?

Grazie.

risposta

3

Fondamentalmente quello che vuoi fare è riempire memcached con i tuoi dati, giusto?

La cosa è che chiedere se una chiave è lì senza recuperare il valore non è molto utile. Vedi questo scenario:

Chiedi se esiste una chiave e lo fa. Subito dopo aver chiesto, i dati della chiave vengono espulsi dalla cache. Mentre la tua risposta sta tornando dicendo che la data è lì, la realtà è che i dati non ci sono. Quindi hai perso tempo a chiedere, perché la risposta è diversa dalla realtà.

Immagino che quello che vuoi fare è chiedere se esiste una chiave, e se così non fosse, riempila con i tuoi dati. Perché non riempi tutti i dati direttamente? A proposito, hai considerato che mentre stai riempiendo memcached con i dati, potresti espellere le chiavi che hai appena inserito in precedenza?

+0

Sì, ma prima devo essere sicuro che non sia già impostato. Perché se è già impostato, quindi mi piacerebbe sprecare la lettura dei dati da una sorgente lenta. Se uso get() per verificare, allora sprecherò l'I/O della rete perché il valore è 1MB è la dimensione. – Matic

+0

@Matic il "problema" è che memcached non è stato progettato pensando alla rete, quindi suppongo che questo sia il motivo per cui tale funzionalità è stata omessa. Se l'I/O di rete è un problema, allora è meglio usare un database kv. Capisco che a volte non vuoi un'assegnazione extra anche se l'IO di rete non è un problema, ma questo non è possibile con memcached. Redis ha una tale caratteristica. – themihai

6

io non sono sicuro se questo è di alcun aiuto a voi, ma è possibile utilizzare

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

tornerà false se la chiave esiste già.

In caso vero viene restituito è possibile utilizzare

Memcache::delete ( string $key) 

per rimuovere la chiave appena impostato. In questo modo non avrai bisogno di recuperare i dati.

+0

E se restituisce true, non dimenticare di fare un Memcached :: delete after –

+0

@Serty Oan: entrambi hanno scritto la stessa ora :) – Thariama

+0

grandi menti pensano allo stesso modo;) –

0

Questo non ha senso dalla prospettiva Memcached. Se si desidera evitare l'origine dati lenta (in base a un processo pianificato, presumo?), Salvare i dati in un'origine dati più rapida ma stabile (ad esempio un file) nel lavoro pianificato. Quando hai bisogno dei dati, prova prima a leggere da Memcached e, se ciò non riesce, leggi il file e salvalo in Memcached.

Memcached non può dare una buona risposta, come pakore hanno già risposto. L'architettura non è pensata per essere un'origine dati stabile.

3

Ho risolto questo problema utilizzando Memcached :: append. Provo ad accodare il valore NULL e se restituisce VERO, significa che la chiave esiste. Se restituisce FALSE significa che la chiave non esiste. Se la chiave esiste, la metterà anche in cima all'elenco LRU.

+1

È necessario assicurarsi che 'Memcached :: OPT_COMPRESSION' sia impostato su false, altrimenti l'operazione fallirà sempre. Inoltre, potrebbe essere preferibile aggiungere la stringa vuota * '' * anziché * NULL * .. –

+0

Per coloro che usano 'Memcache', non' Memcached': il metodo 'append' è disponibile solo in quest'ultimo. – TheFrost

2

Non mi piace il suggerimento di aggiunta/eliminazione perché inserisce dati non validi la cui applicazione può dipendere dalla sua validità. Se la tua applicazione fa questa ipotesi, causerebbe un bug sottile che si verifica ogni tanto a causa delle condizioni di competizione che introduce.

Una soluzione è quella di dare un'occhiata al valore e se i suoi dati temporali fanno finta di non essere lì, ma questo richiederebbe a tutto il codice dell'applicazione di usare la stessa API o di aggiornare l'applicazione stessa, né sono divertenti e un po 'incline agli errori a causa della complessità aggiuntiva.

Se il set di chiavi è abbastanza piccolo, è possibile memorizzarlo in memcached e utilizzare per determinare se recuperare nuovamente i dati dall'origine. Tuttavia, se è grande o grande, il valore di questo metodo è peggiore, quindi è sufficiente ottenere l'intero valore da memcached.

È quindi possibile risolvere il nuovo problema utilizzando un indice per separare le chiavi in ​​più piccole serie (ovviamente un buon modo per coccio questi dati in modo che ogni secchio è una certa dimensione è più facile a dirsi poi fatto.)

L'implementazione consisterebbe nell'utilizzare memcached append o anteporre per mantenere l'elenco di chiavi legate a qualche chiave master o chiave principale che punta a un insieme di sottochiavi che puntano ai tasti stessi :)

In entrambi i casi si sta facendo l'applicazione è sempre più complessa, quindi consiglierei di farlo solo se c'è effettivamente un collo di bottiglia (come sarebbe se le chiavi di esistenza debbano essere controllate spesso su una rete), o se usabil in una preoccupazione (latenza).

Nel mio caso non lo farò perché accedo solo a memcached su localhost e sto usandolo come un'estensione della mia applicazione per memorizzare nella cache risorse che impiegano più di qualche secondo per caricare normalmente.

0

Il modo più semplice è quello di ottenere la chiave data e lanciarla su un valore booleano.

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

Come detto OP, non vuole "ottenere" a causa delle grandi dimensioni dei dati. –

1

Semplicemente non possibile, non è possibile verificare se esiste o meno solo la chiave. Meglio si crea una chiave separata con falsi valori/veri per verificare l'esistenza di chiavi

0

(un po 'tardi per la discussione, ma) in realtà è possibile accedere a tutte le chiavi/valori memorizzati nel memcache, per:

$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

Funzionerebbe, ma è come usare un martello per rompere una noce, il sovraccarico nel farlo ogni volta solo per verificare se esiste una chiave, sarebbe meno oneroso semplicemente ottenere i dati e controllare il codice dei risultati. –

+0

@SteveChilds: diventa utile se non si conosce il valore esatto della chiave. Ad esempio, se stai memorizzando determinate istanze di oggetti nella tua memcache con chiavi simili (object_type-object_id # object_options) e vuoi ricaricare tutte le istanze di object_type-object_id. In realtà, tutto dipende dalla configurazione e dal codice base. – eithed

+0

Ah, ok - sì nel tuo caso d'uso ha senso, ma nel contesto dell'OP, il suo eccessivo. –

4

Mi chiedo perché Memcached non abbia un metodo speciale per questo. Ecco cosa ho trovato dopo alcune considerazioni:

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

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

memcached ora ha il comando cas. è possibile utilizzarlo con una cas unico come 0 per ottenere la esista o risposte 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 

Nella trascrizione di cui sopra, prima io uso il comando set per impostare un ciao chiave con il valore 12345. Poi ottiene indietro, che ha anche restituito un cas univoco 6743. Quindi provo a usare il comando cas per sostituire il valore con niente, ma poiché il cas univoco che ho usato è 0, ho recuperato l'errore EXISTS.

Infine, provo a usare cas per impostare un tasto foo che non esiste e recuperare l'errore NOT_FOUND.

Poiché memcached utilizza un valore di incremento globale per cas univoco, l'utilizzo di 0 è sicuro che non è valido per l'elemento che si sta tentando di impostare e si otterrà l'errore EXISTS se esiste.

Scusa, non ho dimestichezza con il client memcache di php per inserirlo nel codice php.

0

Avevo bisogno di un test affidabile per sapere se usare memcache Set o memcache replace.

Questo è quello che ho finito.

Un'altra opzione sarebbe quella di impostare un socket di rete per la query memcache, ma alla fine finirebbe per fare lo stesso e questa connessione esiste già, salvandomi il sovraccarico di fare e mantenere un'altra connessione come in Joel Chen risposta.

$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); 
} 

Ora, se si dispone di "dati di grandi dimensioni", la soluzione è abbastanza semplice. Usa una seconda chiave in cojoin che contenga qualcosa di semplice come un intero da controllare. Usali sempre insieme e non hai problemi.

$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); 
} 
Problemi correlati