2011-01-03 11 views
5

Wyobraź tabelę o następującej strukturze na PostgreSQL 9.0:PostgreSQL: Ładowanie danych do schematu Gwiazd skutecznie

create table raw_fact_table (text varchar(1000)); 

Dla uproszczenia Wspomnę tylko jedną kolumnę tekstu, w rzeczywistości ma on tuzin. Ta tabela ma 10 miliardów wierszy, a każda kolumna ma wiele duplikatów. Tabela jest tworzona z pliku płaskiego (csv) przy użyciu polecenia KOPIUJ Z.

Aby zwiększyć wydajność Chcę przekształcić do następującej strukturze schematu gwiazda:

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

Fakt tabela zostanie następnie zastąpiony fakt tabeli tak:

create table fact_table (dimension_table_id int); 

mojego obecnego sposobu to zasadniczo uruchomić następujące zapytanie, aby utworzyć tabelę wymiarów:

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

następnie stworzyć wypełnić tabelę wymiaru używam:

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

Następnie trzeba uruchomić następujące zapytanie:

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

Wystarczy wyobrazić sobie straszne wydajność dostaję porównując wszystkie sznurki do wszystkich pozostałych strun kilku czasy.

Na MySQL mogłem uruchomić procedurę składowaną podczas kopiowania z. Może to spowodować utworzenie skrótu łańcucha, a wszystkie kolejne porównania łańcuchów są wykonywane na mieszaniu zamiast długiego nieprzetworzonego łańcucha. Nie wydaje się to możliwe w PostgreSQL, co mam zrobić?

Przykładowe dane byłyby plik CSV zawierający coś takiego (używam cudzysłowu również wokół liczb całkowitych i podwaja):

"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" 
+0

Ile czasu to zajmuje? Jak długo się spodziewałeś? –

+0

Nigdy nie skończyłem go przy użyciu wspomnianej ilości danych. Ale na 15 milionach rzędów zajęło to kilka godzin. Sprawdziłem już wszystkie standardowe funkcje optymalizacji serwera (work_mem itd.), Więc szukam innego sposobu, aby osiągnąć ten sam wynik. – David

+0

Wyślij dane próbki i DDL. –

Odpowiedz

2

Po prostu na pytania: - konieczne jest przekształcenie danych w 1 lub 2 krokach? - Czy możemy modyfikować tabelę podczas konwersji?

Running bardziej prostsza zapytań może poprawić wydajność (i obciążenie serwera robiąc to)

Jedno podejście byłoby:

  1. generować dimension_table (jeśli rozumiem go poprawnie, nie trzeba problemy z wydajnością) (może z dodatkowym tymczasowym polem boolean ...)
  2. repeat: wybierz jedną poprzednio nie wybraną pozycję z tabeli wymiarów, wybierz wszystkie wiersze z tabeli raw_fact_table zawierającej ją i wstaw je do fact_table. Mark dimension_table rekord jako wykonane, a następnie ... Można napisać to jako procedura przechowywana, a to może przekształcić swoje dane w tle, jedzenie minimalnych zasobów ...

lub innym (chyba lepiej):

  1. utwórz fact_table jako KAŻDY rekord z tabeli raw_fact_table I one dimension_id.(Tak w tym dimension_text i dimension_id wierszach)
  2. tworzenia dimension_table
  3. utworzyć po insert wyzwalacz fact_table których:
    • wyszukiwania dla dimension_text w fact_table
    • jeśli nie znaleziono, tworzy nowy rekord w dimension_table
    • aktualizacje dimension_id do tego id
  4. w pętli simle włóż każdy rekord z raw_fact_table do fact_table
+0

Dziękuję za sugestie. Nie sprecyzowałem tego, ale moim jedynym zmartwieniem jest przetwarzanie wszystkich danych tak szybko, jak to możliwe, więc działanie w tle nie ma sensu w mojej konfiguracji (wiem, że jest to bardzo mądre w innych sytuacjach). Problem z drugim podejściem polega na tym, że wyzwalacze nie są wyzwalane na COPY FROM. Sądzę więc, że nie ma powodu, by uruchamiać. Twoje podejście jest jednak nadal bardzo ważne przy użyciu kursora. Jestem jednak niepewny co do wydajności: http://stackoverflow.com/questions/4776127/postgres-surprising-performance-on-updates-using-cursor – David

+0

Co do twojego pierwszego podejścia, nie określiłem w czasie wydajności generowania tabela wymiarów (powinienem to zrobić). Podoba mi się twoje podejście z myślą na odwrót. Stworzę nowy komentarz z podejściem opartym na twoim. – David

+0

create table dimension_table (id seryjny, tekst varchar (1000), raw_fact_table_id bigint [], klucz podstawowy (id)); ------------------- wstaw do wymiaru_wymiar (tekst , raw_fact_table_id) (wybierz tekst, array_agg (raw_fact_table.id) z grupy raw_fact_table według tekstu); Później trzeba będzie znaleźć sposób na aktualizację tabeli raw_fact_table na podstawie identyfikatorów w raw_fact_table_id. Co myślisz? – David

2

Jesteś pomijając kilka szczegółów tam na końcu, ale nie widzę, że koniecznie jest problem. Nie jest dowodem, że wszystkie łańcuchy są faktycznie porównywane do wszystkich innych łańcuchów. Jeśli dokonasz sprzężenia, PostgreSQL może bardzo dobrze wybrać inteligentniejszy algorytm łączenia, taki jak sprzężenie hash, które może dać ci takie samo hashu, które implementujesz w swoim rozwiązaniu MySQL. (Ponownie, Twoje dane są zamglone na ten temat.)

+0

Dzięki za odpowiedź. Mam teraz zaktualizowane pytanie z brakującymi szczegółami. – David

6

Wystarczy wyobrazić sobie straszne wydajność uzyskać porównując wszystkie sznurki do wszystkich innych ciągów kilkakrotnie.

Kiedy robisz to przez chwilę, przestajesz sobie wyobrażać wydajność i zaczynasz ją mierzyć. "Przedwczesna optymalizacja jest źródłem wszelkiego zła".

Co oznacza dla ciebie "miliard"? Dla mnie w USA oznacza to 1 000 000 000 (lub 1 e9). Jeśli dotyczy to również ciebie, prawdopodobnie szukasz od 1 do 7 terabajtów danych.

Moja obecna metoda jest zasadniczo uruchom następującą kwerendę, aby utworzyć tabelę Wymiary:

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

Jak masz zamiar zmieścić 10 miliardów wierszy do tabeli, która używa liczbę całkowitą dla główny klucz? Powiedzmy nawet, że połowa wierszy jest duplikatami. Jak działa ta arytmetyczna praca, kiedy to robisz?

Nie wyobrażaj sobie. Najpierw przeczytaj. Następnie sprawdź.

Przeczytaj Data Warehousing with PostgreSQL. Podejrzewam, że te slajdy prezentacji dadzą ci kilka pomysłów.

Przeczytaj również Populating a Database i zastanów się, które sugestie zastosować.

Testuj z milionem wierszy (1e6), wykonując proces "dziel i rządź". Oznacza to, że nie próbuj załadować miliona na raz; Napisz procedurę, która dzieli ją na mniejsze części. Uruchom

EXPLAIN <sql statement> 

Powiedziałeś, że szacujesz co najmniej 99% zduplikowanych wierszy. Mówiąc ogólnie, istnieją dwa sposoby na pozbycie się duplikatów:

  1. Wewnątrz bazy danych niekoniecznie na tej samej platformie, której używasz do produkcji.
  2. Poza bazą danych, w systemie plików, niekoniecznie ten sam system plików, którego używasz do produkcji.

Jeśli nadal masz załadowane pliki tekstowe, rozważam najpierw wypróbowanie poza bazą danych. Ten unikalny wiersz awk wypisze unikalne linie z każdego pliku. Jest względnie ekonomiczny, ponieważ przechwytuje dane tylko jeden raz.

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

Jeśli naprawdę masz 99% powtórzeń, do końca tego procesu należy zmniejszyły swoje 1 do 7 terabajtów w dół do około 50 koncertów. Po wykonaniu tej czynności można również policzyć każdą unikalną linię i utworzyć plik rozdzielany tabulatorami przed skopiowaniem go do hurtowni danych. To kolejny one-liner:

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

Jeśli masz to zrobić w systemie Windows, użyję Cygwin.

Jeśli musisz to zrobić w bazie danych, starałbym się unikać używania produkcyjnej bazy danych lub serwera produkcyjnego. Ale może jestem zbyt ostrożny. Poruszanie się po kilku terabajtach to droga rzecz.

Ale ja przetestować

SELECT DISTINCT ... 

przed użyciem GROUP BY. Być może uda mi się wykonać kilka testów na dużym zestawie danych, ale prawdopodobnie nie w tym tygodniu. (Zwykle nie pracuję z plikami o rozmiarze terabajta, to trochę interesujące, jeśli możesz poczekać.)

+0

Otrzymuję straszne występy i proszę o konkretne porady dotyczące rozwiązania konkretnego problemu. Tabela raw_fact_table nie ma liczby całkowitej dla klucza podstawowego. Tylko tabele wymiarów są duplikaty 99.XX% w fact_table. Zaimplementowałem już wszystkie porady z linków, które mi wysłałeś. – David

+0

"T" w "ETL" cię zabija. 99% duplikatów oznacza, że ​​celujesz w coś około 100 000 000 wierszy. Będę edytować moją odpowiedź. –

+0

Aby uprościć przykład, wspomniałem tylko, że raw_fact_table ma jedną kolumnę tekstową. W rzeczywistości ma on tuzin, więc twoja metoda usuwania duplikatów nie zadziałałaby, dzięki za wskazanie tego. Zaktualizuję pytanie. Tabela raw_fact_table ma również liczby całkowite i wartości podwójne. – David

1

I zobaczyć kilka sposobów rozwiązywania problemu Istnieje funkcja MD5 w PostgreSQL md5 (string) Oblicza skrótu MD5 sznurka, zwracając wynik w systemie szesnastkowym

wkładkę do dimension_table (wybierz NULL, md5 (tekst), tekst z raw_fact_table grupy tekstem)

dodanie pola md5 do raw_fact_table oraz wybranych id do fact_table z wymiarem wewnętrznym na przyłączenia raw_fact_table (dimension.md5 = raw_fact_table.md5);

indeksów MD5 złożony może pomóc, jak również

Albo można obliczyć MD5 w locie podczas ładowania danych. Na przykład nasze narzędzie ETL Zaawansowane procesor ETL może zrobić to za Ciebie. Ponadto może załadować dane do wielu tabel w tym samym czasie.

Istnieje szereg tutoriali on-line dostępny na naszej stronie internetowej Na przykład ten pokazuje ładowanie powolne zmieniającym wymiar

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

+0

Nie wierzę, że można uruchomić oblicz MD5 podczas uruchamiania KOPIUJ OD (co jest zalecanym sposobem ładowania danych). Jeśli oznacza to, że twoje narzędzie nie używa opcji KOPIUJ Z, to uważam, że jest bezużyteczne, ponieważ ładowanie tego nie zajmie wieków. Muszę powiedzieć, że bardzo sceptycznie podchodzę do rozwiązania ETL bez kodu. To jest w porządku, o ile tylko potrzebuję standardowych rzeczy, ale jeśli kiedykolwiek natknę się na specjalny problem, nie mam kodu, na który można by polegać. – David

+0

Całkowicie zgadzam się z Tobą COPY FROM to najszybszy sposób na załadowanie danych do PostgreSQL. Dlatego używamy go wewnętrznie w zaawansowanym procesorze ETL. Dokumentacja PostgreSQL: COPY TABLE_NAME Z STDIN (STDIN Określa, że ​​dane wejściowe pochodzą z aplikacji klienckiej.) –

+0

Zrobiliśmy co w naszej mocy, aby zrobić to tak szybko, jak to możliwe. Dla każdej bazy danych używamy szybkiego sposobu ładowania danych. (Bezpośrednia/konwencjonalna ścieżka dla oracle, bcp dla SQL Server, kopia dla PostgreSQL, itp.) W rzeczywistości wydrukowaliśmy kod krytyczny oraz zaznaczono i usunięto wszystkie nieskuteczne części. Następnie użyliśmy profilera i zoptymalizowaliśmy wydajność. Ciągle ulepszamy. (Zapoznaj się z naszym forum pomocy technicznej i zwróć uwagę, ile czasu zajmuje rozwiązanie problemu lub wprowadzenie nowej funkcji, niż porównanie z dużymi graczami). –

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

try hash (tekst); i btree (tekst), aby zobaczyć, który z nich jest szybszy

Powiązane problemy