Quali sono i modi possibili per evitare i duplicati quando non è possibile aggiungere un indice univoco

10

Sono bloccato in un problema di concorrenza.

È un tipico problema in cui l'utente invia 2 o 3 transazioni per mantenere alcuni dati che NON DEVONO ESSERE duplicati nel DB, nel caso di un record duplicato dovresti restituire un errore.

Questo problema è facile quando puoi semplicemente aggiungere un indice (univoco) a una colonna in cui memorizzi un hash.

Ma in questo caso, ho una tabella enorme (probabilmente milioni di record) e non posso semplicemente modificare la tabella.

In effetti, abbiamo una colonna in cui memorizziamo un hash dei dati che non dovrebbero essere duplicati, ma non è stato impostato un indice univoco.

Sto provando sul mio codice java per verificare se esiste appena prima del flush, ottenendo ancora duplicati.

Le mie possibili soluzioni per questo sono:

  • Crea un trigger che controlla se l'hash che sto cercando di inserire esiste già sulla tabella.
  • Crea un'altra tabella per memorizzare indici unici per questa tabella e aggiungi una chiave esterna alla tabella principale.
  • Siediti in posizione fetale e piangi
posta rafuru 14.12.2017 - 00:34
fonte

4 risposte

3

Ci sono un paio di possibili scenari facili da risolvere e uno pernicioso che non lo è.

Per un utente che inserisce un valore, inserisce lo stesso valore qualche tempo dopo una semplice SELECT prima che l'INSERT rilevi il problema. Questo funziona per il caso in cui un utente invia un valore e qualche tempo dopo un altro utente invia lo stesso valore.

Se l'utente invia un elenco di valori con duplicati - ad esempio {ABC, DEF, ABC} - in una singola chiamata del codice l'applicazione può rilevare e filtrare i duplicati, forse generando un errore. Dovrai anche verificare che il DB non contenga nessuno dei valori univoci prima dell'inserto.

Lo scenario difficile è quando la scrittura di un utente si trova all'interno del DBMS contemporaneamente alla scrittura di un altro utente e sta scrivendo lo stesso valore. Quindi hai una gara una condizione tra loro. Poiché il DBMS è (molto probabilmente - non si dice quale si sta utilizzando) un sistema di multitasking preventivo qualsiasi attività può essere messa in pausa in qualsiasi momento della sua esecuzione. Ciò significa che l'attività di user1 può controllare che non ci sia una riga esistente, quindi l'attività di user2 può controllare che non ci sia una riga esistente, quindi l'attività di user1 può inserire quella riga, quindi l'attività di user2 può inserire quella riga. Ad ogni punto i compiti sono individualmente felici di fare la cosa giusta. Globalmente si verifica comunque un errore.

Normalmente un DBMS gestirà questo bloccando il valore in questione. In questo problema stai creando una nuova riga quindi non c'è ancora nulla da bloccare. La risposta è un blocco di gamma. Come suggerisce, questo blocca un intervallo di valori, indipendentemente dal fatto che esistano o no. Una volta bloccato, non è possibile accedere a quell'intervallo da un'altra attività finché non viene rilasciato il blocco. Per ottenere blocchi di intervalli devi specificare e il livello di isolamento di SERIALIZABLE . Il fenomeno di un'altra attività di nascosto in una riga dopo che l'attività è stata controllata è noto come record fantasma .

L'impostazione del livello di isolamento su Serializable su tutta l'applicazione avrà implicazioni. La velocità sarà ridotta. Altre condizioni di gara che hanno funzionato abbastanza bene in passato potrebbero iniziare a mostrare errori ora. Ti suggerisco di impostarlo sulla connessione che esegue il tuo codice di induzione duplicata e lasciando il resto dell'applicazione così com'è.

Un'alternativa basata su codice è di controllare dopo la scrittura piuttosto che prima. Quindi fai l'INSERT, quindi conta il numero di righe che hanno quel valore hash. Se ci sono duplicati, esegui il rollback dell'azione. Questo può avere dei risultati perversi. Pronunciare l'attività 1, quindi scrivere l'attività 2. Quindi l'attività 1 controlla e trova un duplicato. Si arretra anche se era il primo. Allo stesso modo entrambe le attività possono rilevare il duplicato e il rollback. Ma almeno avrai un messaggio con cui lavorare, un meccanismo di tentativi e nessun nuovo duplicato. I rollback sono disapprovati, proprio come usare le eccezioni per controllare il flusso del programma. Si noti bene che il tutto funzionante nella transazione verrà ripristinato, non solo la scrittura che induce i duplicati. E dovrai avere transazioni esplicite che potrebbero ridurre la concorrenza. Il controllo duplicato sarà terribilmente lento a meno che non si disponga di un indice sull'hash. Se lo fai, puoi anche renderlo unico!

Come hai commentato la vera soluzione è un indice univoco. Mi sembra che questo dovrebbe rientrare nella finestra di manutenzione (anche se ovviamente si conosce meglio il proprio sistema). Supponiamo che l'hash sia otto byte. Per cento milioni di righe vale circa 1 GB. L'esperienza suggerisce che un ragionevole numero di hardware elaborerebbe queste numerose righe in un minuto o due, al massimo. Il controllo e l'eliminazione duplicati si aggiungeranno a questo, ma possono essere programmati in anticipo. Questo è solo un accostamento, però.

    
risposta data 14.12.2017 - 04:43
fonte
2

In fact, we have a column where we store a hash of the data that should not be duplicated but a unique index was not set.

Controllare le collisioni hash è un buon primo passo, ma attenzione, non puoi garantire che lo stesso programma produca lo stesso hash sugli stessi dati se viene riavviato . Molte funzioni di hash "veloci" utilizzano un prng integrato che viene seminato all'ora di inizio del programma. Usa un hash crittografico se l'hash deve essere sempre lo stesso, non importa come, come fai in questa applicazione. Nota che non hai bisogno di un hash crittografico valido o sicuro.

Il secondo passo consiste nel verificare effettivamente l'uguaglianza dei dati, poiché anche le migliori funzioni di hash a volte provocano collisioni, poiché si riduce (di solito) l'entropia dei dati.

Passaggio 1: controlla se ottieni una collisione su un hash crittografico

Passaggio 2: se gli hash corrispondono, verifica che i dati effettivi siano gli stessi

    
risposta data 14.12.2017 - 03:02
fonte
2

Crea una nuova tabella con una chiave primaria univoca

Sul lato client, inizia a generare GUID per ogni record in modo da poter rilevare resend semplici.

Metti i nuovi record nella nuova tabella in modo che almeno tu stia bene per i nuovi dati in arrivo.

Avere una colonna nella nuova tabella "CheckedAgainstOldData"

Avere un'attività di back-end che esegue qualsiasi controllo di hash lento è per vedere se può trovare un duplicato nei vecchi dati e impostare il flag di conseguenza, rifiutare i duplicati a questo punto, inviando una notifica al client.

Nel frattempo hai un'altra attività di back-end che sposta i dati dalla vecchia alla nuova tabella, controllando la presenza di duplicati con il tuo controllo hash e generando il GUID.

Puoi lasciare questa attività in esecuzione per diversi giorni (se necessario), trasferendo i dati senza tempi di inattività.

Una volta completato il trasferimento, è possibile disattivare la lenta procedura "CheckedAgainstOldData". e trasferire tutti i dati su una singola tabella.

Francamente però se il problema è grave come descrivi e il software è vecchio, allora avrai migliaia di duplicati.

    
risposta data 24.01.2018 - 20:55
fonte
1

Supponendo che i dati che provengono dall '"utente" significhi qualcuno seduto alla tastiera e che i due siano generati da due utenti che inseriscono gli stessi dati nello stesso momento. Prova ad aggiungere una funzione che provoca un ritardo casuale all'inizio del trigger. Dare un minimo di quanto tempo ci vuole per scrivere un nuovo record sul tavolo e probabilmente un massimo di non più di un nano-centesimo o giù di lì. In questo modo, quando ricevi richieste dupe, dovrebbe essere fatto il primo e l'attivazione dell'esistenza dovrebbe ripristinare il risultato corretto. (Chiarimento: ogni chiamata dovrebbe avere il proprio tempo di ritardo casuale unico, lungo gli stessi principi del protocollo ALOHA )

    
risposta data 14.12.2017 - 03:43
fonte

Leggi altre domande sui tag