Ho costantemente lottato per risolvere i problemi di duplicazione dei dati in modo efficiente (memorizzazione di dati da qualsiasi fonte a RDBMS). La mia preoccupazione principale è accelerare gli inserimenti / l'elaborazione in batch.
Scenario: leggo dati da diverse fonti, principalmente in json formato e ho bisogno di elaborarli in modo (input: documenti nosql), dove memorizzo solo diverse righe 1 una volta nel database. Il numero di insertibles è diverso, ma intorno a 1-10 milioni di righe. La velocità di lettura è veloce, posso leggere più velocemente che posso, su altrettanti discussioni come posso.
1 Diverso di solito significa determinati campi nell'input il documento dovrebbe essere unico se è lo stesso insieme. Come se io devi salvare i prodotti e avere questi campi:
{
"manufacturer": "Xy manufacturer",
"manufacturerReference": "Manufacturer's product id, barcode = identifier",
"name": "..",
"price": ...
}
Tutte le combinazioni di manufacturer
e manufacturerReference
dovrebbe essere rappresentato solo una volta nel set di dati dopo l'elaborazione.
Il mio primo pensiero è stato rapidamente abbandonato, volevo solo creare qualcosa di unico
indici nel database e gestiscono eccezioni chiave duplicate, ma la maggior parte
del tempo semplicemente non funzionerà. Tutti i database hanno limiti di lunghezza
le chiavi univoche (byte di SQL Server 900) e utilizzando varchar(255+)
i campi lo riempiono rapidamente, anche riempire i registri con eccezioni sembra una soluzione sciatta.
Quindi, il problema sorge quando voglio salvare i dati. L'ingenuo l'approccio sarebbe:
- Cerca di scoprire se l'insertable è un duplicato o meno con una query come
select 1 from X where manufacturer=$1 and manufacturerReference=$2
- Se non abbiamo una riga esistente, fai l'inserto, altrimenti ritorna.
Questa soluzione può essere molto lenta, quindi avremmo bisogno di indici su tutto il si suppone che siano campi univoci, che possono rendere le dimensioni del datastore aumentare drasticamente con un sacco di righe (anche, definendo indici su anche le grandi righe varchar non dovrebbero essere appropriate).
Questo si è evoluto in un'altra soluzione, in cui utilizzo un hashing appropriato
algoritmo (md5 di solito) per unire efficacemente i campi ( fingerprint = md5(manufacturer + manufacturerReference)
) in un singolo campo, che
può essere memorizzato in un luogo piccolo (per md5, binary(16)
). Dopo, posso
definire indici univoci su fingerprint
. Inserimento di file ancora
rimangono gli stessi, o continuo a ingoiare gli errori del database, o io
fai un controllo dell'esistenza in anticipo.
Il che mi porta a un altro problema, il blocco del database e i deadlock. Fare selezioni e inserimenti nella stessa transazione con più thread contemporaneamente sarà lento ed errato (come ho sperimentato). Le prestazioni che voglio quando elaboro milioni di righe non dovrebbero essere le ore (che attualmente sono).
Come si risolve questo problema (best practice?)? Se dovessi memorizzare i dati in un negozio NoSQL, il mio problema potrebbe essere facilmente risolto (i nuovi inserimenti per lo stesso fingerprint
creano solo una nuova versione), ma i target sono MySQL
, SQL Server
e PostgreSQL
di solito .