Come cercare rapidamente un elenco molto ampio di stringhe / record su un database


32

Ho il seguente problema: ho un database contenente più di 2 milioni di record. Ogni record ha un campo stringa X e voglio visualizzare un elenco di record per i quali il campo X contiene una determinata stringa. Ogni record ha una dimensione di circa 500 byte.

Per renderlo più concreto: nella GUI della mia applicazione ho un campo di testo in cui posso inserire una stringa. Sopra il campo di testo ho una tabella che mostra i (primi N, ad esempio 100) record che corrispondono alla stringa nel campo di testo. Quando scrivo o cancello un carattere nel campo di testo, il contenuto della tabella deve essere aggiornato al volo.

Mi chiedo se esiste un modo efficace per farlo utilizzando strutture di indice appropriate e / o memorizzazione nella cache. Come spiegato sopra, voglio solo visualizzare i primi N elementi che corrispondono alla query. Pertanto, per N abbastanza piccolo, non dovrebbe essere un grosso problema caricare gli elementi corrispondenti dal database. Inoltre, la memorizzazione nella cache degli elementi nella memoria principale può velocizzare il recupero.

Penso che il problema principale sia come trovare rapidamente gli oggetti corrispondenti, data la stringa del modello. Posso fare affidamento su alcune strutture DBMS o devo creare personalmente un indice in memoria? Qualche idea?

MODIFICARE

Ho eseguito un primo esperimento. Ho diviso i record in diversi file di testo (al massimo 200 record per file) e ho inserito i file in directory diverse (ho usato il contenuto di un campo dati per determinare l'albero delle directory). Finisco con circa 50000 file in circa 40000 directory. Ho quindi eseguito Lucene per indicizzare i file. La ricerca di una stringa con il programma demo Lucene è piuttosto veloce. La divisione e l'indicizzazione hanno richiesto alcuni minuti: questo è assolutamente accettabile per me perché è un set di dati statici che voglio interrogare.

Il prossimo passo è integrare Lucene nel programma principale e utilizzare gli hit restituiti da Lucene per caricare i relativi record nella memoria principale.


2
2 milioni di record * 500 byte = 1 GB di dati. Questo è un sacco di dati da cercare, in qualunque modo si va su di esso - è ogni valore di X rischia di essere unico, o si hanno molti record con lo stesso valore di X?

1
Sarebbe anche un sacco di dati da tentare di archiviare in memoria come cache per un rapido recupero. Ciò equivarrebbe a più di 1 GB per sessione utente.
maple_shaft

Il mio commento precedente presuppone un'applicazione Web. Si tratta di un'applicazione web?
maple_shaft

È un'applicazione desktop. I valori nei record non sono necessariamente unici. Inoltre, sto cercando sottostringhe non per una corrispondenza esatta.
Giorgio,

@maple_shaft: vorrei solo memorizzare nella cache i record a cui ho avuto accesso di recente. Se cambio la stringa di query e un record corrisponde ancora, è ancora nella cache.
Giorgio,

Risposte:


20

Invece di inserire i tuoi dati nel DB, puoi tenerli come un insieme di documenti (file di testo) separatamente e mantenere il link (percorso / url ecc.) Nel DB.

Ciò è essenziale perché la query SQL in base alla progettazione sarà molto lenta sia nella ricerca di sottostringa che nel recupero.

Ora, il tuo problema è formulato come, dovendo cercare i file di testo che contengono l'insieme di stringhe. Ci sono due possibilità qui.

  1. Corrispondenza sotto-stringa Se il BLOB di testo è una singola puntura o parola (senza spazi bianchi) ed è necessario cercare al suo interno una stringa secondaria. In tali casi è necessario analizzare ogni file per trovare i migliori file possibili corrispondenti. Uno utilizza algoritmi come l'algoritmo Boyer Moor. Vedi questo e questo per i dettagli. Questo equivale anche a grep, perché grep usa cose simili all'interno. Ma potresti comunque fare almeno 100+ grep (nel peggiore dei casi 2 milioni) prima di tornare.

  2. Ricerca indicizzata Qui stai assumendo che il testo contenga un insieme di parole e che la ricerca sia limitata a lunghezze di parole fisse. In questo caso, il documento viene indicizzato su tutte le possibili occorrenze di parole. Questo è spesso chiamato "Ricerca full-text". Esistono numerosi algoritmi per eseguire questa operazione e un numero di progetti open source che possono essere utilizzati direttamente. Molti di essi supportano anche la ricerca con caratteri jolly, ricerca approssimativa ecc. Come di seguito:
    a. Apache Lucene: http://lucene.apache.org/java/docs/index.html
    b. OpenFTS: http://openfts.sourceforge.net/
    c. Sphinx http://sphinxsearch.com/

Molto probabilmente se hai bisogno di "parole fisse" come query, l'approccio due sarà molto veloce ed efficace.


2
Questo è un concetto interessante ma sembra improbabile che uno sviluppatore possa facilmente cercare tra 1 GB di dati testuali più velocemente e in modo più efficiente di un motore di database. Persone molto più intelligenti di te e io abbiamo lavorato su ottimizzatori di query per fare proprio questo ed è un po 'ingenuo pensare che in qualche modo sia possibile farlo in modo più efficiente.
maple_shaft

4
@maple_shaft Gli esempi che ho fornito non sono motori di database RDBMS. Sono più simili a "motori di ricerca" se si desidera chiamarlo. Esiste un'enorme differenza concettuale tra la raccolta di un elenco da un indice (o una tabella hash) rispetto alla ricerca di nuovo da 1 GB di dati ogni volta che viene generata una query. Quindi quello che sto suggerendo non è una modifica minore.
Dipan Mehta,

Sembra un'idea interessante ma mi chiedo come funzionerebbe. Avrei più di 2 000 000 di file, ciascuno delle dimensioni di circa mezzo chilobyte. O stai suggerendo di avere più di un record per file? Quale sarebbe la differenza tra un database?
Giorgio,

Non sono convinto che questo avrebbe necessariamente prestazioni migliori dell'indice full-text SQL.
Kirk Broadhurst,

@Giorgio - sì, è così che funzionano i motori di ricerca full-text. La differenza chiave qui è una pagina preindicizzata rispetto alla ricerca in memoria (di nuovo ogni volta che arriva una query).
Dipan Mehta,

21

La tecnologia che stai cercando è l'indicizzazione full-text. La maggior parte dei RDBMS ha una sorta di funzionalità integrate che potrebbero funzionare qui, oppure potresti usare qualcosa come Lucene se vuoi diventare più fantasioso e / o semplicemente eseguirlo in memoria.


1
A mio avviso, le opzioni full text in qualsiasi RDBMS sono una soluzione per far sì che faccia qualcosa per cui non è progettato: "cerca in un mucchio di dati non strutturati non correlati". Se stai costruendo un motore di ricerca, semplicemente non usi un RDBMS. Può funzionare per set di dati di piccole dimensioni ma consente di eseguire il ridimensionamento di qualsiasi tipo di ridimensionamento. La ricerca tra pile di dati non strutturati non è un chiodo, quindi non usare un martello. Usa lo strumento giusto per il lavoro.
Pieter B,

8

Hai considerato un trie ? Fondamentalmente costruisci un albero usando prefissi comuni, quindi tutte le parole che iniziano con le stesse lettere sono figli dello stesso nodo. Se hai intenzione di supportare la corrispondenza su qualsiasi sottostringa, dovrai generare una sorta di indice permutato e costruire il tuo trie da quello. Ciò potrebbe finire per far esplodere i requisiti di archiviazione, però.


1
SÌ! Stavo pensando a una struttura ad albero e mi sono ricordato che c'era qualcosa di simile che poteva adattarmi, ma non ricordavo il trie perché non li avevo mai usati. Per quanto riguarda i requisiti di archiviazione: ricorda che devo recuperare solo le prime N voci (ad es. N = 100) perché non ha senso popolare una tabella con 20000 hit. Quindi ogni nodo del trie dovrebbe indicare al massimo N voci. Inoltre, ho dimenticato di dire che ho bisogno di un accesso veloce ma non ho bisogno di un aggiornamento veloce, perché i dati vengono caricati solo una volta. L'idea del trie su un indice permutato potrebbe davvero funzionare!
Giorgio,

1
Buona risposta, ma come noti, un trie è ottimo per abbinare l' inizio delle tue parole ma diventerà rapidamente complesso e molto grande se si abbina a qualsiasi sottostringa ...
Kirk Broadhurst

Come primo esperimento, ho cercato di costruire l'insieme di tutte le sottostringhe che compaiono nelle stringhe che devo cercare che, se capisco correttamente, corrispondono ai percorsi del trie. Ho ricevuto un'eccezione di memoria insufficiente (con 256 M di heap per la JVM) a sottostringhe di lunghezza 6. Quindi temo che questa soluzione non sia fattibile, a meno che non stia facendo qualcosa di sbagliato.
Giorgio,

5

Vorrei aggiungere alla risposta di Wyatt Barnett che una soluzione RDBMS con indicizzazione full-text sulla colonna appropriata funzionerà, ma se si desidera utilizzare una cache locale di record precedentemente recuperati, è necessario un piano per utilizzare questi record memorizzati nella cache a tuo vantaggio.

Un'opzione è quella di raccogliere gli identificativi univoci di questi record che ESPLICITAMENTE non si desidera recuperare dalla query e includerli, possibilmente in a NOT INo a NOT EXISTS.

Avvertenza, tuttavia, l'utilizzo NOT INo NOT EXISTStende a non essere economico e PUO 'influenzare negativamente le prestazioni della query o il piano di query a seconda del motore di database che si sta utilizzando. Esegui un piano esplicativo sulla tua query finale per assicurarti che tutti gli indici sulle colonne interessate vengano utilizzati.

Inoltre, non fa male fare un confronto delle prestazioni tra i due approcci per vedere quale è più veloce. Potresti essere sorpreso di scoprire che mantenere una cache locale e filtrare esplicitamente quelli dalla tua query potrebbe avere prestazioni peggiori rispetto a una query ottimizzata che recupera tutti i record.


maple_shaft e @Wyatt Barnett: grazie mille per i suggerimenti. Dovrò leggere un po 'e provare diverse soluzioni. Non tutti i database supportano l'indicizzazione completa, invece MySQL (che sto attualmente utilizzando) ( dev.mysql.com/doc/refman/5.5/it/fulltext-search.html ). Proverò a fare alcuni test e poi riferirò qui.
Giorgio,

2

Nel caso lo avessi perso. Se usi Lucene per il tuo database anziché per la ricerca di testo supportata nel DB, dovrai fare molta attenzione quando modifichi il tuo DB. Come ti assicuri di poter avere atomicità quando devi apportare modifiche sia al DB che alle risorse esterne (Lucene)? Sì, si può fare, ma ci sarà molto lavoro.

In breve, stai perdendo il supporto transazionale DB se metti Lucene nel tuo schema di dati.


1
Il problema, come affermato, non sembra comunque adatto ad un RDMS.
Pieter B,

1

Hai considerato la Sfinge? http://sphinxsearch.com se puoi usare uno strumento di terze parti sarebbe l'ideale per quello che stai cercando di ottenere, è molto più efficiente nella ricerca full text rispetto a qualsiasi RDBMS che ho usato personalmente.


3
e il voto negativo è per?
twigg,

1

È alquanto strano che nessuna delle risposte abbia presentato il termine "indice invertito" , la tecnologia alla base di tutte le soluzioni simili ad Apache Lucene e altre.

L'indice invertito è una mappatura da parole a documenti ("indice invertito a livello di record") o persino posizioni precise delle parole all'interno del documento ("indice invertito a livello di parola").

Le operazioni logiche AND e OR sono banali da implementare. Se si dispone di posizioni precise delle parole, è possibile cercare parole adiacenti, rendendo così possibile la ricerca di frasi.

Quindi, pensa a un indice contenente tuple (parola, file, posizione). Quando hai ad es. ("Invertito", "foo.txt", 123), controlla semplicemente se ("indice", "foo.txt", 124) fa parte dell'indice per cercare la frase completa "indice invertito" .

Mentre non ti consiglio di reimplementare un motore di ricerca full-text da zero, è utile sapere come funzionano le tecnologie come Apache Lucene.

Quindi, la mia raccomandazione è di imparare come funzionano gli indici invertiti e scegliere una tecnologia che li utilizza come Apache Lucene. Quindi almeno hai una solida comprensione di cosa si può fare e cosa non si può fare.

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.