2013-07-02 14 views
6

Vorrei ricevere feedback e suggerimenti su due approcci che sto considerando di implementare gli indici ricercabili utilizzando i set ordinati di Redis.Indicizzazione utilizzando i set ordinati Redis

Situazione e obiettivo

Al momento hanno alcune tabelle di valori-chiave stiamo memorizzazione in Cassandra, e che noi vorrebbero avere indici per. Ad esempio, una tabella conterrà record di persone e la tabella di Cassandra avrà come chiave primaria id e l'oggetto serializzato come valore. L'oggetto avrebbe campi come first_name, last_name, last_updated e altri.

Quello che vogliamo è essere in grado di fare ricerche come "last_name = 'Smith' AND first_name> 'Joel'", "last_name < 'Aaronson'", "last_name = 'Smith' AND first_name = 'Winston' " e così via. Le ricerche dovrebbero restituire gli ID delle partite in modo da poter recuperare gli oggetti da Cassandra. Sto pensando che le ricerche di cui sopra potrebbero essere fatte con un singolo indice, ordinato lessicograficamente da last_name, first_name e last_updated. Se abbiamo bisogno di alcune ricerche usando un ordine diverso (ad esempio "first_name = 'Zeus'") possiamo avere un indice simile che permetterebbe loro (ad es. First_name, last_updated).

Stiamo cercando di utilizzare Redis per questo, perché dobbiamo essere in grado di gestire un numero elevato di scritture al minuto. Ho letto su alcuni modi comuni Redis ordinata set sono utilizzati, e venire con due possibili implementazioni:

Opzione 1: un unico insieme ordinato per indice

Per il nostro indice cognome, first_name, last_updated, avremmo un insieme ordinato in Redis sotto gli indici chiave: people: last_name: first_name: last_updated, che conterrebbe stringhe con il formato last_name: first_name: last_updated: id. Per esempio:

Smith: joel: 1372761839,444: 0azbjZRHTQ6U8enBw6BJBw

(Per il separatore potrei usare '::', piuttosto che ':' o qualcos'altro per lavorare meglio con l'ordinamento lessicografico, ma cerchiamo di ignorare che, per ora)

A tutti gli elementi viene assegnato un punteggio 0 in modo che il set ordinato venga ordinato in ordine lessicografico dalle stringhe stesse. Se poi voglio fare una query come "last_name = 'smith' AND first_name < 'bob'", avrei bisogno di ottenere tutti gli elementi nella lista che precede 'smith: bob'.

Per quanto posso dire, vi sono le seguenti svantaggi di questo approccio:

  1. Non esiste una funzione Redis per selezionare un intervallo in base al valore della stringa. Questa funzione, chiamata ZRANGEBYLEX, è stata proposta da Salvatore Sanfilippo allo https://github.com/antirez/redis/issues/324, ma non è implementata, quindi dovrei trovare gli endpoint usando le ricerche binarie e ottenere l'intervallo da solo (magari usando Lua, o al livello dell'applicazione con Python che è la lingua che stiamo usando per accedere a Redis).
  2. Se vogliamo includere un time-to-live per le voci di indice, sembra che il modo più semplice per farlo sarebbe avere un'attività pianificata regolarmente che attraversa l'intero indice e rimuove gli elementi scaduti.

Opzione 2: piccoli insiemi ordinati, ordinati per LAST_UPDATED

Questo approccio sarebbe simile, tranne che avremmo molti, più piccolo, insiemi allineati, con ciascuno che ha un valore di tempo simile, come LAST_UPDATED per i punteggi. Ad esempio, per lo stesso cognome, first_name, ultimo indice aggiornato, avremmo un insieme ordinato per ogni combinazione last_name, first_name. Ad esempio, la chiave potrebbe essere indici: people: last_name = smith: first_name = joel, e avrebbe una voce per ogni persona che abbiamo chiamato Joel Smith. Ogni voce avrebbe come nome l'id e il suo punteggio il valore last_updated. Es .:

valore: 0azbjZRHTQ6U8enBw6BJBw; Punteggio: 1372761839.444

I principali vantaggi di questo sono (a) le ricerche in cui sappiamo tutti i campi tranne LAST_UPDATED sarebbe molto facile, e (b) l'attuazione di un time-to-live sarebbe molto semplice, utilizzando lo ZREMRANGEBYSCORE.

L'inconveniente, che sembra molto grande per me è:

  1. Sembra che ci sia molto di più di complessità nella gestione e la ricerca in questo modo. Ad esempio, avremmo bisogno dell'indice per tenere traccia di tutte le sue chiavi (nel caso, ad esempio, vogliamo pulire in un punto) e farlo in modo gerarchico. Una ricerca come "last_name < 'smith'" richiederebbe prima di tutto l'elenco di tutti i cognomi per trovare quelli che vengono prima di smith, quindi per ognuno di quelli che guardano tutti i nomi che contiene, quindi per ognuno di quelli ottenere tutti gli elementi dal set ordinato. In altre parole, un sacco di componenti da costruire e preoccuparsi.

avvolgendo

così sembra a me la prima opzione sarebbe meglio, nonostante i suoi svantaggi. Apprezzerei molto qualsiasi feedback riguardante queste due o altre possibili soluzioni (anche se dovessero usare qualcosa di diverso da Redis).

risposta

7
  1. Sconsiglio vivamente l'uso di Redis per questo. Memorizzi una grande quantità di dati del puntatore in più e se decidi di voler eseguire query più complicate come, SELECT WHERE first_name LIKE 'jon%', avrai dei problemi. Dovrai anche creare indici extra, molto grandi che attraversano più colonne, nel caso in cui tu voglia cercare due campi allo stesso tempo. In pratica, dovrai mantenere l'hacking e riprogettare un framework di ricerca. Faresti molto meglio a usare Elastic Search o Solr o uno qualsiasi degli altri framework già creati per fare ciò che stai cercando di fare. Redis è fantastico e ha molti buoni usi. Questo non è uno di loro.

  2. Attenzione a parte, per rispondere alla tua domanda attuale: penso che ti sarebbe meglio servire usando una variante della tua prima soluzione. Usa un singolo set ordinato per indice, ma converti le tue lettere in numeri. Converti le tue lettere in qualche valore decimale. È possibile utilizzare il valore ASCII o semplicemente assegnare ogni lettera a un valore 1-26 in ordine lessicografico, assumendo che si stia utilizzando l'inglese. Standardizza, in modo che ogni lettera occupi la stessa lunghezza numerica (quindi, se 26 è il tuo numero più grande, 1 verrebbe scritto "01"). Quindi aggiungili semplicemente insieme a una virgola decimale e usali come punteggio per indice (ad esempio "cappello" sarebbe ".080120"). Questo ti permetterà di avere una mappatura 1-a-1 correttamente ordinata tra parole e questi numeri. Quando cerchi, converti da lettere a numeri e poi sarai in grado di utilizzare tutte le belle funzioni ordinate di Redis come ZRANGEBYSCORE senza doverle riscrivere.Le funzioni di Redis sono scritte molto, molto ottimamente, quindi stai molto meglio usandole quando possibile invece di scrivere le tue.

4

È possibile utilizzare il mio progetto python-stdnet per questo, fa tutto l'indicizzazione per voi. Per esempio:

class Person(odm.StdModel): 
    first_name = odm.SymbolField() 
    last_name = odm.SymbolField() 
    last_update = odm.DateTimeField() 

Una volta che un modello è registered with a redis backend, si può fare questo:

qs = models.person.filter(first_name='john', last_name='smith') 

così come

qs = models.person.filter(first_name=('john','carl'), last_name=('smith','wood')) 

e molto altro

Il filtraggio è veloce come tutti gli ID sono già in set.

+0

Il [aiuto su come non essere uno spammer] (http://stackoverflow.com/help/promotion) è chiaro che "è necessario rivelare la propria affiliazione nelle vostre risposte." Ho modificato la tua risposta di conseguenza. – Louis

0

È possibile controllare redblade, può indice di manutenzione automaticamente per voi ed è scritto da Node.JS.

//define schema 
redblade.schema('article', { 
    "_id"   : "id" 
    , "poster"  : "index('user_article')" 
    , "keywords" : "keywords('articlekeys', return +new Date()/60000 | 0)" 
    , "title"  : "" 
    , "content"  : "" 
}) 


//insert an article 
redblade.insert('article', { 
    _id  : '1234567890' 
    , poster  : 'airjd' 
    , keywords : '信息技术,JavaScript,NoSQL' 
    , title  : '测试用的SLIDE 标题' 
    , content : '测试用的SLIDE 内容' 
}, function(err) { 

}) 


//select by index field or keywords 
redblade.select('article', { poster:'airjd' }, function(err, articles) { 
    console.log(articles[0]) 
}) 

redblade.select('article', { keywords: 'NoSQL' }, function(err, articles) { 
    console.log(articles[0]) 
}) 
Problemi correlati