Modo efficiente per cercare i dati in base a più attributi

0

Stiamo costruendo un sistema in cui accumuliamo i dati da molti dei nostri servizi interni, li elaboriamo e generiamo un insieme di dati chiamati lavori salvati nel database. La nostra applicazione client in esecuzione sui sistemi client richiede periodicamente questi lavori e nella risposta vengono inviati i lavori idonei per il cliente richiesto. Per ogni richiesta del cliente è necessario cercare lavori qualificati dalla tabella dei lavori in base ai parametri della richiesta del cliente.

Esempio:

Lavoro:

#1
{
    "id": "1",
   "searchFields":{
      "key1":"value1",
      "key2":"value2",
      "key3":"value3"
   },
   "job": <SOME COMMAND>
}

#2
{
    "id": "2",
   "searchFields":{
      "key3":"value3",
      "key4":"value4",
   },
   "job": <SOME COMMAND>
}

#3
{
    "id": "3",
   "searchFields":{
      "key5":"value5",
      "key6":"value6",
   },
   "job": <SOME COMMAND>
}

Come dovrebbe funzionare la ricerca?

Abbiamo un set di attributi nella richiesta del client, i valori di questi attributi dovrebbero corrispondere ai valori degli attributi nel campo di ricerca di lavoro "searchField". Se la richiesta del cliente ha attributi "key1", "key2" e "key3" e searchField del lavoro ha "key1" e "key2", questo job è qualificato solo se il valore di "key1" e "key2" di entrambe le richieste del client e Job corrispondenze searchField.

Richieste client:

# 1
{
      "key1":"value1",
      "key2":"value2",
      "key3":"value3"
}
Job #1 is qualified. Job #2 is not qualified because request input does not have "key4".


# 2
{
      "key1":"value1",
      "key2":"some_different_value",
      "key3":"value3"
}
No jobs are qualified because value of key2 doesn't match with any job

# 3
{
      "key1":"value1",
      "key2":"value2",
      "key3":"value3",
      "key4":"value4"
}

Job #1 and #2 are qualified.


# 3
{
      "key2":"value2",
      "key3":"value3",
      "key4":"value4",
      "key5":"value5",
      "key6":"value6",
}

Job #2 and #3 are qualified. Job #1 is not qualified because "key1" does not exists in input data

Abbiamo già realizzato un prototipo per questo sistema utilizzando il database MySQL, ma riteniamo che MySQL non sia adatto per questo tipo di sistemi. La tabella dei lavori è molto ampia e continua a crescere (più di 1.000.000 di record aggiunti ogni giorno) e la ricerca di lavori basati su più attributi solo utilizzando query SQL standard (senza campi indicizzati) non è efficiente. Inoltre, gli attributi nella richiesta del cliente e nel campo di ricerca di Job sono dinamici. Non abbiamo un set fisso di attributi su cui lavorare. Nuovi attributi possono essere aggiunti o rimossi in qualsiasi momento, quindi se utilizziamo query SQL piuttosto che gestire gli attributi dinamici sarebbe ingombrante.

Cosa abbiamo provato?

Abbiamo creato combinazioneKey e hashKey di tutti gli attributi di valori-chiave in searchField per ogni Job e salvati nel database insieme al lavoro.

Come vengono generati combinazioneKey e hashkey,

Job:

{
    "id": "1",
   "searchFields":{
      "key1":"value1",
      "key2":"value2",
      "key3":"value3"
   },   
   "job": <SOME COMMAND>
}

combinationKey="key1 :: :: key2 key3" HashKey = sha256Of ( "chiave1 = valore1 :: chiave2 = valore2 :: key3 = value3")

E salva lavoro come,

{
    "id": "1",
    "combinationKey": "key1::key2::key3",
    "hashKey":<Hash_Key>,
   "searchFields":{
      "key1":"value1",
      "key2":"value2",
      "key3":"value3"
   },   
   "job": <SOME COMMAND>
}

Quando riceviamo la richiesta del cliente, recuperiamo le chiavi di combinazione univoche dalla tabella dei lavori (scansione completa della tabella o cache) e generiamo hash per gli attributi delle richieste in entrata,

Chiavi di combinazione uniche dalla tabella dei lavori: "key1 :: key2 :: key3", "key4 :: key5" ... ecc.

Richiesta cliente:

{
      "key1":"value1",
      "key2":"value2",
      "key3":"value3",
      "key4":"value4"
}

Scorrere tra tutte le chiavi di combinazione e generare l'elenco di hashKey per gli attributi di richiesta in entrata e una volta generate le hashKeys, cercare Lavori nella tabella Lavori che corrisponde a questi hashKeys.

Questo approccio sembra funzionare bene nel nostro prototipo, ma ritengo che non sia abbastanza efficiente, poiché con la crescita dei dati di Jobs esiste la possibilità che il conteggio di combinazioni uniche possa crescere in modo esponenziale e per ogni richiesta client, calcolando hashkey per tutte le combinazioni sarebbe intensivo di calcolo.

Di cosa abbiamo bisogno?

  1. Il modo più efficiente e facile per cercare la tabella dei lavori basata sugli attributi della richiesta del client in entrata secondo i nostri requisiti.
  2. Ricerca coerente e accurata. Non vogliamo usare motori di ricerca come Elasticsearch perché abbiamo bisogno di dati accurati e non di analisi.
  3. Il miglior database adatto a questo tipo di sistemi.
  4. Qualsiasi azienda (come facebook, google) utilizza già questo tipo di sistemi, quindi possiamo analizzare il loro approccio.

Qualsiasi aiuto sarebbe apprezzato.

    
posta user3820681 10.07.2017 - 14:43
fonte

4 risposte

1

Le tue esigenze sembrano aver bisogno di capacità di indicizzazione / ricerca full text che la maggior parte degli RDBM (incluso MySQL) può fare. Ma se la ricerca a testo integrale è la maggior parte di quello che farai, potresti stare meglio con un DB di tipo noSQL come Solr. Ovviamente potresti usare entrambi, se hai bisogno di cose che gli RDBM SQL fanno bene e noSQL non funziona bene. Ma dalla tua descrizione dei tuoi bisogni, sembra che noSQL sia la direzione da seguire.

    
risposta data 10.07.2017 - 17:06
fonte
0

Ho fatto qualcosa di simile, ma non ho db, ho un flusso di dati che alcuni ritengo, altri no e i dati sono eterogenei.

Il trucco che ho realizzato è semplificare la ricerca.

"key3": "qualcosa" viene trasformato in "key3: something"

In questo modo, è possibile inserire molti criteri di ricerca in un unico dizionario ei valori nel dizionario sono HashSet di riferimenti di dataline, che ha il "key3" uguale a "qualcosa"

Quindi il risultato è come cercare 3 stringhe, ottenendo 3 HasSet. Iterando attraverso ogni HashSet e i suoi riferimenti sono assegnati al Dizionario, ogni aggiornamento di dataline sta incrementando il valore.

E il risultato sono semplicemente tutti gli elementi di questo dizionario con 3 risultati.

L'ho trovato che svolge ragionevolmente il compito che dovevo risolvere ...

    
risposta data 10.07.2017 - 17:44
fonte
0

Se la velocità di recupero è della massima importanza, puoi prendere in considerazione la possibilità di mantenere una struttura di dati separata e indicizzata per eseguire ricerche rapidamente e senza scansioni di tabelle.

Crea due nuove tabelle:

KeyNames table:

KeyID - identity - clustered index and primary key

KeyName - string - unique non-clustered indexed

KeyValues tabella:

KeyID - FK to KeyNames.KeyID

KeyValue - A value for the key

JobID - FK to a record in your Jobs table that has a key with the specified value

The combination of KeyID, KeyValue, and JobID should be unique and indexed as the clustering key. This allows a complete covered index query.

Quando devi trovare un lavoro che corrisponde a una chiave, devi usare qualcosa come

SELECT j.*
FROM   Jobs      j
WHERE EXISTS 
      (   
          SELECT 0 
          FROM KeyValues v
          JOIN KeyName   n ON n.KeyID = v.KeyID
          WHERE v.JobID   = j.JobID
          AND  n.KeyName  = 'Key1'
          AND  n.KeyValue = 'Value1'
      )

Se devi cercare più di una chiave, aggiungi semplicemente alla clausola WHERE:

SELECT j.*
FROM   Jobs      j
WHERE EXISTS 
      (   
          SELECT 0 
          FROM KeyValues v
          JOIN KeyName   n ON n.KeyID = v.KeyID
          WHERE v.JobID   = j.JobID
          AND  n.KeyName  = 'Key1'
          AND  n.KeyValue = 'Value1'
      )
AND EXISTS 
      (
          SELECT 0 
          FROM KeyValues v
          JOIN KeyName   n ON n.KeyID = v.KeyID
          WHERE v.JobID   = j.JobID
          AND  n.KeyName  = 'Key2'
          AND  n.KeyValue = 'Value2'
      )

Questo evita qualsiasi scansione della tabella della grande tabella Jobs . Otterrai due ricerche di indice su KeyNames e due indici indice coperti su KeyValues , quindi un indice cerca per ogni riga in Jobs da restituire. Inoltre, le tabelle indicizzate sono molto strette (non contengono molte informazioni), quindi sarà possibile correggere molti record su una singola pagina indice. La combinazione di queste funzionalità di progettazione comporterà un numero di I / O molto inferiore rispetto a una scansione della tabella Jobs ampia e non indicizzata.

Come bonus, questo design ti consente di creare tante chiavi quante ne vuoi, in fase di esecuzione, senza alcuna modifica alla struttura dei dati. Quindi gli attributi sono totalmente dinamici, il che era uno dei tuoi requisiti.

L'unico svantaggio è che è leggermente più difficile inserire e aggiornare i dati, ma rispetto all'approccio hashing, probabilmente è in realtà più semplice e può essere eseguito interamente nelle stored procedure senza operazioni di hashing con utilizzo intensivo della CPU.

    
risposta data 11.07.2017 - 03:17
fonte
0

Posiziona un indice composito sul valore chiave
Made ID the PK

Penso che funzioni per un database relazionale

Per prima cosa elimina i record che non hanno tutti i tasti

select a.ID
  from ( SELECT table.ID, count(*) as count
           from items 
           left join ( VALUES ('key1'), ('key3') ) searchKeys (key) 
             on table.key = searchKeys.key 
           group by table.ID
       ) a 
  join ( SELECT table.ID, count(*) as count
           from items 
           group by table.ID
       ) b 
    on a.ID = b.ID and a.count = b.count 
  join items as c 
    on c.ID = a.ID 
   and c.key = 'key1'
   and c.value = 'value1'
  join items as d 
    on d.ID = a.ID 
   and d.key = 'key2'
   and d.value = 'value2'
    
risposta data 11.07.2017 - 20:38
fonte