Qual è l'algoritmo per gli articoli in scadenza nella memorizzazione dei valori-chiave?


10

Stavo pensando a come gli attuali archivi di valori-chiave implementano la "data di scadenza" per gli articoli. Attualmente ho 2 varianti per questo nella mia mente:

  1. non fanno nulla (mantengono i dati scaduti) e controllano solo quando, ad esempio, ottieni una chiave. Il problema qui è che se la memoria è limitata, gli elementi scaduti non verranno eliminati.
  2. mantengono strutture di dati aggiuntive per essere in grado di "scadere prima". Vedo che può essere fatto con qualcosa del genere:

    storage_data = dict(key -> [value, expire_timestamp])
    expire_tree = SomeBinaryLikeTree(expire_timestamp -> [keys])
    

Risposte:


6

Il problema dell'eliminazione delle voci scadute nella cache è molto simile alla garbage collection , meno l'intera complessità del conteggio dei riferimenti.

Le persone di Nasza-Klasa hanno proposto l'algoritmo O (1) per Memcache come segue:

Sembra che molte persone abbiano creduto per qualche motivo che la liberazione di voci scadute non potesse essere eseguita in O (1), o addirittura che richiedesse operazioni Omega (N). L'uso di un heap o di altre strutture di dati di coda prioritarie può ovviamente darti O (log N), ma la patch sotto mira a O (1). Ciò si ottiene avendo un bucket per ogni secondo e inserendo ciascuna voce in un bucket appropriato osservando il tempo di scadenza. Quindi ad ogni secondo liberiamo semplicemente elementi dal secchio successivo. Questo è ovviamente O (1) tempo ammortizzato, ma può succedere che tu abbia molti elementi che scadono nello stesso momento, quindi la patch offre un limite fisso per il numero di operazioni che sei disposto a eseguire per una richiesta, per rendere la raccolta dei rifiuti più fluida.

Vedi l' intera proposta con il codice allegato .


Grazie. Ho anche pensato alla soluzione "secchio" come un modo. Inoltre non ci sono problemi con "troppi oggetti nel bucket" dato che puoi usare l'algoritmo "prendi bucket che non hai preso l'ultima volta e torna quando hai finito".
Kostiantyn Rybnikov,

@k_bx: ecco perché propongono un doppio elenco di link, quindi puoi tornare ai bucket precedenti.
Vartec,

Se i bucket sono qualcosa come i secondi, non hai bisogno di elenchi collegati. Per andare in precedenza, basta semplicemente diminuire il tasto :)
Kostiantyn Rybnikov

@k_bx: diminuisce la chiave di quanto? un secondo? cosa succede se un secchio non completamente svuotato precedente era 5 minuti fa? diminuire di 1 secondo 300 volte?
vartec,

Al primo avvio del server, si avvia una variabile denominata current_expire_bucket su un valore. Quindi, esegui cleanup a partire da current_expire_bucket, terminando attualmente secondo. Al termine della pulizia, dormi per un breve periodo. Se il server si arresta, attraverserai nuovamente lo stesso "bucket di scadenza", sì, ma dovrebbe accadere solo agli arresti del server.
Kostiantyn Rybnikov,

7

Suppongo che l'archiviazione del valore-chiave sia troppo grande per scorrere solo tutte le coppie di kv per scoprire quale può essere scaduto. Suppongo anche che ogni accesso in lettura aggiorni il timestamp di scadenza, quindi sono scaduti solo gli elementi a cui non si è avuto accesso da un po 'di tempo.

La sfida è trovare in modo efficiente tutti i record che possono essere scaduti (ogni volta che è necessaria la pulizia), ma anche aggiornare in modo efficiente il timestamp di scadenza su ogni accesso in lettura (quindi dobbiamo trovare la chiave nella struttura utilizzata per la scadenza).

La mia proposta: raggruppare expiry_timestamps in bucket; ad esempio, se gli oggetti vivono per 8 ore, crea un secchio all'ora. Quei secchi sono tenuti in un elenco collegato; quando si verifica la scadenza, il primo bucket viene svuotato e l'elenco ridotto. Il numero di secchi è la durata della vita / intervallo di pulizia. Ogni bucket contiene un hashSet di tutte le chiavi che devono essere scadute. L'iterazione su tutti i tasti in un hashset è abbastanza efficiente.

Durante l'accesso in lettura, il programma controlla a quale bucket si trova la chiave e a quale bucket appartiene ora. Nella maggior parte dei casi, è lo stesso bucket, quindi non sono necessarie ulteriori azioni. In caso contrario, rimuovere la chiave dal vecchio bucket (rimuovere da un set di hash è efficiente) e inserirla nel nuovo bucket.

   +--------------+   +--------------+   +--------------+
-->+ Expiry 08:00 +-->+ Expiry 09:00 +-->+ Expiry 10:00 +
   | KeySet       |   | KeySet       |   | KeySet       |
   +--------------+   +--------------+   +--------------+
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.