Domanda

Immaginate una tabella con la seguente struttura su PostgreSQL 9.0:

create table raw_fact_table (text varchar(1000));

Per ragioni di semplificazione cito solo una colonna di testo, in realtà ha una dozzina. Questa tabella ha 10 miliardi di righe e ogni colonna ha un sacco di duplicati. La tabella viene creata da un file flat (CSV) utilizzando copiare.

Per aumentare le prestazioni voglio convertire al seguente struttura schema a stella:

create table dimension_table (id int, text varchar(1000));

La tabella dei fatti sarebbe quindi essere sostituito con una tabella dei fatti come il seguente:

create table fact_table (dimension_table_id int);

Il mio attuale metodo è quello di eseguire in sostanza la seguente query per creare la tabella dimensioni:

Create table dimension_table (id int, text varchar(1000), primary key(id));

poi per creare riempire l'uso tabella della dimensione I:

insert into dimension_table (select null, text from raw_fact_table group by text);

In seguito ho bisogno di eseguire la seguente query:

select id into fact_table from dimension inner join raw_fact_table on (dimension.text = raw_fact_table.text);

Basta immaginare le prestazioni orribili ottengo confrontando tutte le stringhe a tutte le altre corde più volte.

In MySQL ho potuto eseguire una stored procedure durante la COPIA DA. Questo potrebbe creare un hash di una stringa e tutte le successive confronto stringa viene fatto sul hash invece della lunga serie crudo. Questo non sembra essere possibile in PostgreSQL, cosa devo fare allora?

I dati campione sarebbe un file CSV contenente qualcosa come questo (io uso le virgolette anche intorno interi e doppie):

"lots and lots of text";"3";"1";"2.4";"lots of text";"blabla"
"sometext";"30";"10";"1.0";"lots of text";"blabla"
"somemoretext";"30";"10";"1.0";"lots of text";"fooooooo"
È stato utile?

Soluzione

Solo per le domande: - è necessario da convertire i dati in 1 o 2 gradini? -? Maggio abbiamo modificare la tabella durante la conversione

Esecuzione query più semplici di può migliorare le prestazioni (e il carico del server, mentre farlo)

Un approccio potrebbe essere:

  1. generare dimension_table (se ho ben capito, non avete problemi di prestazioni con questo) (magari con un campo booleano aggiuntivo temporaneo ...)
  2. ripetizione: scegliere una voce in precedenza non selezionata da dimension_table, selezionare ogni righe da raw_fact_table che lo contengono e inserirli in fact_table. Segnare record di dimension_table come fatto, e dopo ... È possibile scrivere questo come una stored procedure, ed è possibile convertire i dati in background, mangiare risorse minime ...

O un altro (probabilmente migliore):

  1. creare fact_table come da registrare ogni raw_fact_table e uno dimension_id. (Quindi compresi dimension_text e le righe dimension_id)
  2. creare dimension_table
  3. Crea un inserto dopo trigger per fact_table che:
    • ricerche per dimension_text in fact_table
    • se non trovato, crea un nuovo record in dimension_table
    • aggiornamenti dimension_id a questo ID
  4. in un ciclo simle, inserire ogni record da raw_fact_table a fact_table

Altri suggerimenti

Basta immaginare le prestazioni orribili Ottengo confrontando tutte le stringhe a tutti altre corde più volte.

Quando hai fatto questo un po ', si smette di immaginare le prestazioni, e si inizia a misurarlo. "Ottimizzazione prematura è la radice di tutti i mali".

Che cosa significa "miliardi" significa per te? Per me, negli Stati Uniti, significa miliardo (o 1E9). Se questo è vero anche per voi, probabilmente stai guardando tra 1 e 7 terabyte di dati.

Il mio metodo attuale è quello essenzialmente eseguire la seguente query per creare la Tabella delle dimensioni:

Create table dimension_table (id int, text varchar(1000), primary key(id));

Come farai in forma 10 miliardi di righe in una tabella che utilizza un numero intero per una chiave primaria? Diciamo pure che la metà delle righe sono duplicati. Come funziona l'aritmetica quando lo si fa?

non immaginate. Leggi prima. Poi prova.

Data Warehousing con PostgreSQL . Ho il sospetto che queste diapositive di presentazione vi darà qualche idea.

dati in un database , e considerare che i suggerimenti per l'attuazione .

Prova con un milione (1E6) le righe, a seguito di un processo di "divide et impera". Che è, non cercare di caricare un milione alla volta; scrivere una procedura che si rompe in su in porzioni più piccole. Esegui

EXPLAIN <sql statement>

Hai detto si stima almeno il 99% le righe duplicate. In generale, ci sono due modi per sbarazzarsi dei gonzi

  1. All'interno di un database, non necessariamente la stessa piattaforma utilizzata per la produzione.
  2. Al di fuori di un database, nel file system, non necessariamente lo stesso file system utilizzato per la produzione.

Se avete ancora i file di testo che è stato caricato, mi piacerebbe prendere in considerazione prima tentando di fuori del database. Questo awk one-liner uscita volontà linee uniche di ogni file. E 'relativamente economico, in quanto rende solo passaggio sui dati.

awk '!arr[$0]++' file_with_dupes > file_without_dupes

Se avete davvero 99% gonzi, entro la fine di questo processo, si dovrebbe avere ridotto i tuoi 1 a 7 terabyte fino a circa 50 concerti. E, dopo aver fatto questo, è anche possibile numerare ogni linea unica e creare un file delimitato da tabulazioni prima di copiarlo nel data warehouse. Questo è un altro uno-liner:

awk '{printf("%d\t%s\n", NR, $0);}' file_without_dupes > tab_delimited_file

Se si deve fare questo in Windows, mi piacerebbe utilizzare Cygwin .

Se dovete fare questo in un database, mi piacerebbe cercare di evitare di utilizzare il database di produzione o il vostro server di produzione. Ma forse sono troppo prudente. Muoversi diversi terabyte in giro è una cosa costosa da fare.

Ma mi piacerebbe test

SELECT DISTINCT ...

prima di utilizzare GROUP BY. Potrei essere in grado di fare alcuni test su un grande insieme di dati per voi, ma probabilmente non questa settimana. (Io di solito non lavoro con i file terabyte. È una specie di interessante. Se si può aspettare.)

Si sta omettendo alcuni dettagli lì alla fine, ma non vedo che ci sia necessariamente un problema. Non è in evidenza che tutte le stringhe sono in realtà rispetto a tutte le altre corde. Se fate un join, PostgreSQL potrebbe benissimo scegliere un intelligente algoritmo di unirsi, come ad esempio un join hash, che potrebbe dare lo stesso hashing che si implementa te stesso nella soluzione MySQL. (Anche in questo caso, i dati sono confusa su quello.)

-- add unique index
CREATE UNIQUE INDEX uidx ON dimension_table USING hash(text);
-- for non case-sensitive hash(upper(text))

prova hash (testo); e btree (testo) per vedere quale è più veloce

I un vedo diversi modi di risolvere il tuo problema C'è la funzione md5 in PostgreSQL md5 (stringa) Calcola l'hash MD5 di stringa, restituendo il risultato in esadecimale

inserto in dimension_table (selezionare nullo, MD5 (testo), testo dal gruppo raw_fact_table da testo)

aggiungere md5 campo in raw_fact_table pure selezionare id in fact_table dalla dimensione interna join raw_fact_table on (dimension.md5 = raw_fact_table.md5);

Indici su MD5 archiviato potrebbe aiutare così

In alternativa, è possibile calcolare MD5 al volo durante il caricamento dei dati. Per esempio il nostro strumento di ETL processore Advanced ETL può farlo per voi. In più è in grado di caricare i dati in più tabelle contemporaneamente.

C'è un certo numero di tutorial on-line disponibile sul nostro sito web Per esempio questo dimostra caricamento lento che cambia dimensione

http://www.dbsoftlab.com/online-tutorials/advanced-etl-processor/advanced-etl-processor-working-with-slow-changing-dimension-part-2.html

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top