2012-04-04 10 views
17

Ho una piccola domanda.Redis 10 volte maggiore utilizzo di memoria rispetto ai dati

Sto tentando di memorizzare un elenco di parole in redis. La performance è grandiosa.

Il mio approccio consiste nel creare un set chiamato "parole" e aggiungere ogni nuova parola tramite "sadd".

Ecco il problema quando si aggiunge un file che è 15.9mb e contiene circa un milione di parole il processo redis-server consuma 160mb di ram. Come mai sto usando 10 volte la memoria, c'è un modo migliore per affrontare questo problema?

Grazie in anticipo

risposta

75

Bene questo è previsto per una memorizzazione efficiente dei dati: le parole devono essere indicizzate in memoria in una struttura dati dinamica di celle collegate da puntatori. La dimensione dei metadati della struttura, dei puntatori e della frammentazione interna degli allocatori di memoria è la ragione per cui i dati richiedono molta più memoria di un file flat corrispondente.

Un set Redis è implementato come tabella hash. Questo include:

  • un array di puntatori crescente geometricamente (potenze di due)
  • una seconda matrice può essere necessaria quando rimaneggiamento incrementale è attiva
  • celle dell'elenco singole-linked rappresentano le voci nella tabella hash (3 puntatori, 24 byte per voce)
  • involucri oggetto Redis (uno per valore) (16 byte per voce)
  • stessi dati reali (ognuno preceduto da 8 byte per dimensioni e portata)
01.235.164,106 mila

Tutte le dimensioni sopra indicate sono fornite per l'implementazione a 64 bit. Contabilizzando il sovraccarico dell'alloggiamento di memoria, risulta che Redis richiede almeno 64 byte per elemento impostato (in cima ai dati) per una versione recente di Redis utilizzando l'allocatore jemalloc (> = 2.4)

Redis fornisce memory optimizations per alcuni tipi di dati, ma non coprono set di stringhe. Se hai davvero bisogno di ottimizzare il consumo di memoria dei set, ci sono trucchi che puoi usare comunque. Non lo farei per soli 160 MB di RAM, ma se dovessi avere dati più grandi, ecco cosa puoi fare.

Se non sono necessarie le funzionalità unione, intersezione, differenza degli insiemi, è possibile memorizzare le parole in oggetti hash. Il vantaggio è che gli oggetti hash possono essere ottimizzati automaticamente da Redis usando zipmap se sono abbastanza piccoli. Il meccanismo zipmap è stato sostituito da ziplist in Redis> = 2.6, ma l'idea è la stessa: utilizzare una struttura di dati serializzata che può adattarsi alla cache della CPU per ottenere sia le prestazioni che un footprint di memoria compatto.

Per garantire che gli oggetti di hash siano sufficientemente piccoli, i dati potrebbero essere distribuiti secondo un meccanismo di hashing.Se c'è bisogno per memorizzare gli oggetti 1M, l'aggiunta di una parola potrebbe essere attuato nel modo seguente:

  • hash modulo 10000 (fatto sul lato client)
  • parole HMSET: [hashnum] [parola] 1

invece di memorizzare:

words => set{ hi, hello, greetings, howdy, bonjour, salut, ... } 

è possibile memorizzare:

words:H1 => map{ hi:1, greetings:1, bonjour:1, ... } 
words:H2 => map{ hello:1, howdy:1, salut:1, ... } 
... 

Per recuperare o verificare l'esistenza di una parola, è la stessa (cancellarla e utilizzare HGET o HESISTS).

Con questa strategia, significativo risparmio di memoria può essere fatto disponibile il modulo di hash è scelto in base alla configurazione zipmap (o ZipList per Redis> = 2.6):

# Hashes are encoded in a special way (much more memory efficient) when they 
# have at max a given number of elements, and the biggest element does not 
# exceed a given threshold. You can configure this limits with the following 
# configuration directives. 
hash-max-zipmap-entries 512 
hash-max-zipmap-value 64 

Attenzione: il nome questi parametri sono cambiati con Redis> = 2.6.

Qui, modulo 10000 per articoli 1M significa 100 articoli per oggetti hash, che garantiranno che tutti siano memorizzati come zipmap/elenchi di zip.

+0

Risposta affascinante e dettagliata; Non lo sapevo. Grazie a @Didier! –

+0

Va bene grazie mille sono abbastanza positivo che questo risolverà i miei problemi. E sì per 160mb è una multa, ma mi aspetto di lavorare con un massimo di 1gb di dati di parole semplici e non voglio che questo picco a 10 GB. Grazie ancora, apprezza la risposta dettagliata. – cwoebker

+2

@Didier - Ottima risposta! Un paio di correzioni anche se a) Le voci di Hashtable sono una singola lista collegata, non doppia, l'overhead di 24 byte è corretta anche se b) Il wrapper degli oggetti di Redis non si applica a ciascuna voce di set/hash. Si applica solo alla coppia chiave/valore di livello superiore, in modo che l'overhead sia costante c) Si potrebbe voler indicare che zipmap è deprecato in 2.6/unstable e che la lista zip fa la cosa equivalente. –

2

Hai provato persistendo il database (BGSAVE per esempio), chiudendo il server verso il basso e ottenere il backup? A causa del comportamento di frammentazione, quando viene eseguito il backup e popola i suoi dati dal file RDB salvato, potrebbe richiedere meno memoria.

Inoltre: Quale versione di Redis con cui si lavora? Dai un'occhiata a this blog post - dice che la frammentazione è stata parzialmente risolta a partire dalla versione 2.4.

4

Per quanto riguarda i miei esperimenti, è meglio memorizzare i dati all'interno di una tabella hash/dizionario. il caso migliore che abbia mai raggiunto dopo un sacco di benchmarking è di archiviare all'interno delle voci dei dati di hashtable che non superano le 500 chiavi.

Ho provato set di stringhe standard/get, per 1 milione di chiavi/valori, la dimensione era 79 MB. È molto grande nel caso in cui tu abbia grandi numeri come 100 milioni che useranno circa 8 GB.

Ho provato gli hash per memorizzare gli stessi dati, per lo stesso milione di chiavi/valori, la dimensione era sempre più piccola di 16 MB.

Provate a caso se qualcuno ha bisogno del codice di benchmarking, mandatemi una mail

Problemi correlati