Czasami bazy danych PostgreSQL muszą importować duże ilości danych w jednym lub w minimalnej liczbie kroków. Jest to powszechnie znane jako zbiorcze importowanie danych, w przypadku którego źródłem danych jest zwykle jeden lub więcej dużych plików. Ten proces może być czasami niedopuszczalnie powolny.
Istnieje wiele przyczyn tak słabej wydajności:indeksy, wyzwalacze, klucze obce, klucze podstawowe identyfikatora GUID, a nawet dziennik zapisu z wyprzedzeniem (WAL) mogą powodować opóźnienia.
W tym artykule omówimy kilka wskazówek dotyczących najlepszych praktyk dotyczących zbiorczego importowania danych do baz danych PostgreSQL. Mogą jednak zaistnieć sytuacje, w których żadna z tych wskazówek nie będzie skutecznym rozwiązaniem. Zalecamy czytelnikom rozważenie zalet i wad każdej metody przed jej zastosowaniem.
Wskazówka 1:Zmień tabelę docelową na tryb bez logowania
Dla PostgreSQL 9.5 i nowszych, tabela docelowa może być najpierw zmieniona na UNLOGGED, a następnie ponownie na LOGGED po załadowaniu danych:
ALTER TABLE <target table> SET UNLOGGED
<bulk data insert operations…>
ALTER TABLE <target table> LOGGED
Tryb UNLOGGED zapewnia, że PostgreSQL nie wysyła operacji zapisu tabeli do dziennika zapisu z wyprzedzeniem (WAL). Może to znacznie przyspieszyć proces ładowania. Jednak ponieważ operacje nie są rejestrowane, danych nie można odzyskać, jeśli podczas ładowania wystąpi awaria lub nieczyste zamknięcie serwera. PostgreSQL automatycznie skróci każdą niezalogowaną tabelę po ponownym uruchomieniu.
Ponadto tabele niezalogowane nie są replikowane na serwery w trybie gotowości. W takich przypadkach istniejące replikacje należy usunąć przed załadowaniem i odtworzyć po załadowaniu. W zależności od ilości danych w węźle podstawowym i liczby stanów gotowości, czas odtwarzania replikacji może być dość długi i nie do zaakceptowania przez wymagania wysokiej dostępności.
Zalecamy następujące sprawdzone metody zbiorczego wstawiania danych do tabel, które nie są rejestrowane:
- Tworzenie kopii zapasowej tabeli i danych przed zmianą jej w tryb niezalogowany
- Odtwarzanie dowolnej replikacji na serwery rezerwowe po zakończeniu ładowania danych
- Korzystanie z niezalogowanych wstawek zbiorczych dla tabel, które można łatwo ponownie wypełnić (np. duże tabele przeglądowe lub tabele wymiarów)
Wskazówka 2:Usuń i odtwórz indeksy
Istniejące indeksy mogą powodować znaczne opóźnienia podczas wstawiania danych zbiorczych. Dzieje się tak, ponieważ po dodaniu każdego wiersza odpowiedni wpis indeksu również musi zostać zaktualizowany.
W miarę możliwości zalecamy usuwanie indeksów z tabeli docelowej przed rozpoczęciem wstawiania zbiorczego i ponowne tworzenie indeksów po zakończeniu ładowania. Ponownie, tworzenie indeksów na dużych tabelach może być czasochłonne, ale generalnie będzie szybsze niż aktualizowanie indeksów podczas ładowania.
DROP INDEX <index_name1>, <index_name2> … <index_name_n>
<bulk data insert operations…>
CREATE INDEX <index_name> ON <target_table>(column1, …,column n)
Może warto tymczasowo zwiększyć maintenance_work_mem parametr konfiguracyjny tuż przed utworzeniem indeksów. Zwiększona pamięć robocza może pomóc w szybszym tworzeniu indeksów.
Inną opcją bezpiecznej zabawy jest utworzenie kopii tabeli docelowej w tej samej bazie danych z istniejącymi danymi i indeksami. Tę nowo skopiowaną tabelę można następnie przetestować za pomocą funkcji wstawiania zbiorczego dla obu scenariuszy:upuszczania i odtwarzania indeksów lub dynamicznej ich aktualizacji. Metodę, która zapewnia lepszą wydajność, można następnie zastosować do tabeli na żywo.
Wskazówka 3:Usuń i odtwórz klucze obce
Podobnie jak indeksy, ograniczenia klucza obcego mogą również wpływać na wydajność ładowania zbiorczego. Dzieje się tak, ponieważ każdy klucz obcy w każdym wstawionym wierszu musi być sprawdzony pod kątem istnienia odpowiedniego klucza podstawowego. Za kulisami PostgreSQL używa wyzwalacza do wykonania sprawdzania. Podczas ładowania dużej liczby wierszy ten wyzwalacz musi być uruchamiany dla każdego wiersza, zwiększając obciążenie.
O ile nie ograniczają tego reguły biznesowe, zalecamy usunięcie wszystkich kluczy obcych z tabeli docelowej, załadowanie danych w jednej transakcji, a następnie odtworzenie kluczy obcych po zatwierdzeniu transakcji.
ALTER TABLE <target_table>
DROP CONSTRAINT <foreign_key_constraint>
BEGIN TRANSACTION
<bulk data insert operations…>
COMMIT
ALTER TABLE <target_table>
ADD CONSTRAINT <foreign key constraint>
FOREIGN KEY (<foreign_key_field>)
REFERENCES <parent_table>(<primary key field>)...
Po raz kolejny zwiększając maintenance_work_mem parametr konfiguracyjny może poprawić wydajność odtwarzania ograniczeń kluczy obcych.
Wskazówka 4:Wyłącz wyzwalacze
Wyzwalacze INSERT lub DELETE (jeśli proces ładowania obejmuje również usuwanie rekordów z tabeli docelowej) mogą powodować opóźnienia w zbiorczym ładowaniu danych. Dzieje się tak, ponieważ każdy wyzwalacz będzie miał logikę, którą należy sprawdzić, i operacje, które muszą zostać zakończone zaraz po wstawieniu lub usunięciu każdego wiersza.
Zalecamy wyłączenie wszystkich wyzwalaczy w tabeli docelowej przed zbiorczym ładowaniem danych i włączenie ich po zakończeniu ładowania. Wyłączenie WSZYSTKICH wyzwalaczy obejmuje również wyzwalacze systemowe, które wymuszają sprawdzanie ograniczeń klucza obcego.
ALTER TABLE <target table> DISABLE TRIGGER ALL
<bulk data insert operations…>
ALTER TABLE <target table> ENABLE TRIGGER ALL
Wskazówka 5:Użyj polecenia KOPIUJ
Zalecamy użycie KOPIUJ PostgreSQL polecenie, aby załadować dane z jednego lub więcej plików. COPY jest zoptymalizowany pod kątem zbiorczego ładowania danych. Jest bardziej wydajny niż uruchamianie dużej liczby instrukcji INSERT lub nawet wielowartościowych instrukcji INSERT.
COPY <target table> [( column1>, … , <column_n>)]
FROM '<file_name_and_path>'
WITH (<option1>, <option2>, … , <option_n>)
Inne korzyści wynikające z używania COPY to:
- Obsługuje import plików tekstowych i binarnych
- Ma charakter transakcyjny
- Pozwala określić strukturę plików wejściowych
- Może warunkowo ładować dane za pomocą klauzuli WHERE
Wskazówka 6:Użyj wielowartościowych INSERT
Uruchamianie kilku tysięcy lub kilkuset tysięcy instrukcji INSERT może być złym wyborem w przypadku zbiorczego ładowania danych. Dzieje się tak, ponieważ każde polecenie INSERT musi zostać przeanalizowane i przygotowane przez optymalizator zapytań, przejść przez wszystkie sprawdzanie ograniczeń, uruchomić jako oddzielną transakcję i zalogować się do WAL. Użycie wielowartościowej pojedynczej instrukcji INSERT może zaoszczędzić to obciążenie.
INSERT INTO <target_table> (<column1>, <column2>, …, <column_n>)
VALUES
(<value a>, <value b>, …, <value x>),
(<value 1>, <value 2>, …, <value n>),
(<value A>, <value B>, …, <value Z>),
(<value i>, <value ii>, …, <value L>),
...
Na wydajność wielowartościowych funkcji INSERT mają wpływ istniejące indeksy. Zalecamy usunięcie indeksów przed uruchomieniem polecenia i późniejsze ich ponowne utworzenie.
Innym obszarem, o którym należy pamiętać, jest ilość pamięci dostępnej dla PostgreSQL do uruchamiania wielowartościowych INSERTów. Gdy uruchomiona jest wielowartościowa INSERT, duża liczba wartości wejściowych musi zmieścić się w pamięci RAM i jeśli nie ma wystarczającej ilości dostępnej pamięci, proces może się nie powieść.
Zalecamy ustawienie efektywnego_rozmiaru_cache parametr na 50% i shared_buffer parametr do 25% całkowitej pamięci RAM urządzenia. Ponadto, aby być bezpiecznym, uruchamia serię wielowartościowych INSERT, z każdą instrukcją zawierającą wartości dla 1000 wierszy.
Wskazówka 7:Uruchom ANALIZĘ
Nie jest to związane z poprawą wydajności zbiorczego importu danych, ale zdecydowanie zalecamy uruchomienie ANALIZA polecenie w tabeli docelowej zaraz po import luzem. Duża liczba nowych wierszy znacznie zniekształci rozkład danych w kolumnach i spowoduje, że wszelkie istniejące statystyki w tabeli będą nieaktualne. Gdy optymalizator zapytań używa przestarzałych statystyk, wydajność zapytań może być niedopuszczalnie niska. Uruchomienie polecenia ANALIZA zapewni aktualizację wszystkich istniejących statystyk.
Ostateczne myśli
Zbiorczy import danych może nie mieć miejsca codziennie w przypadku aplikacji bazy danych, ale uruchamianie zapytań ma wpływ na wydajność. Dlatego konieczne jest jak najlepsze zminimalizowanie czasu ładowania. Jedną z rzeczy, które administratorzy baz danych mogą zrobić, aby zminimalizować wszelkie niespodzianki, jest przetestowanie optymalizacji obciążenia w środowisku programistycznym lub pomostowym o podobnych specyfikacjach serwera i konfiguracjach PostgreSQL. Każdy scenariusz ładowania danych jest inny i najlepiej wypróbować każdą metodę i znaleźć tę, która działa.