PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Przewodnik po partycjonowaniu danych w PostgreSQL

Co to jest partycjonowanie danych?

W przypadku baz danych z bardzo dużymi tabelami partycjonowanie jest wspaniałą i sprytną sztuczką dla projektantów baz danych, która poprawia wydajność bazy danych i znacznie ułatwia konserwację. Maksymalny rozmiar tabeli dozwolony w bazie danych PostgreSQL to 32 TB, jednak jeśli nie będzie działał na jeszcze nie wynalezionym komputerze z przyszłości, problemy z wydajnością mogą pojawić się na tabeli zawierającej tylko jedną setną tej przestrzeni.

Partycjonowanie dzieli tabelę na wiele tabel i zazwyczaj odbywa się w taki sposób, że aplikacje uzyskujące dostęp do tabeli nie zauważają żadnej różnicy, poza szybszym dostępem do potrzebnych danych. Dzieląc tabelę na wiele tabel, chodzi o to, aby wykonywanie zapytań musiało przeskanować znacznie mniejsze tabele i indeksy w celu znalezienia potrzebnych danych. Niezależnie od tego, jak wydajna jest strategia indeksowania, skanowanie indeksu tabeli o pojemności 50 GB zawsze będzie znacznie szybsze niż indeksu tabeli o pojemności 500 GB. Dotyczy to również skanowania tabel, ponieważ czasami skanowanie tabel jest po prostu nieuniknione.

Wprowadzając podzieloną na partycje tabelę do planera zapytań, należy wiedzieć i zrozumieć kilka rzeczy na temat samego planera zapytań. Zanim jakiekolwiek zapytanie zostanie faktycznie wykonane, planista zapytań weźmie je i zaplanuje najbardziej efektywny sposób uzyskania dostępu do danych. Dzieląc dane między różne tabele, planista może zdecydować, do których tabel uzyskać dostęp, a które całkowicie zignorować, w zależności od zawartości każdej tabeli.

Odbywa się to poprzez dodanie ograniczeń do podzielonych tabel, które definiują, jakie dane są dozwolone w każdej tabeli, a przy dobrym projekcie planer zapytań może skanować niewielki podzbiór danych, a nie całość.

Czy tabela powinna być podzielona?

Partycjonowanie może drastycznie poprawić wydajność na stole, jeśli zostanie wykonane prawidłowo, ale jeśli zostanie wykonane źle lub gdy nie jest potrzebne, może pogorszyć wydajność, a nawet uniemożliwić jej użycie.

Jak duży jest stół?

Nie ma prawdziwej sztywnej reguły określającej, jak duża musi być tabela, zanim partycjonowanie stanie się opcją, ale w oparciu o trendy dostępu do bazy danych użytkownicy bazy danych i administratorzy zaczną dostrzegać, jak wydajność określonej tabeli zaczyna spadać wraz ze wzrostem jej rozmiarów. Ogólnie rzecz biorąc, partycjonowanie powinno być brane pod uwagę tylko wtedy, gdy ktoś mówi „Nie mogę zrobić X, ponieważ tabela jest za duża”. Dla niektórych hostów 200 GB może być właściwym czasem na partycjonowanie, dla innych może to być czas na partycjonowanie, gdy osiągnie 1 TB.

Jeśli tabela zostanie uznana za „zbyt dużą”, czas przyjrzeć się wzorcom dostępu. Znając aplikacje, które uzyskują dostęp do bazy danych, lub monitorując dzienniki i generując raporty zapytań za pomocą czegoś takiego jak pgBadger, możemy zobaczyć, w jaki sposób uzyskuje się dostęp do tabeli i w zależności od tego, jak uzyskuje się dostęp, możemy mieć opcje dobrej strategii partycjonowania.

Aby dowiedzieć się więcej o pgBadger i jak z niego korzystać, zapoznaj się z naszym poprzednim artykułem o pgBadger.

Czy rozdęcie stołu jest problemem?

Zaktualizowane i usunięte wiersze powodują martwe krotki, które ostatecznie muszą zostać oczyszczone. Stoły do ​​odkurzania, czy to ręcznie, czy automatycznie, sprawdzają każdy rząd w stole i określa, czy ma on zostać zregenerowany, czy pozostawiony sam. Im większa tabela, tym dłużej trwa ten proces i tym więcej zasobów systemowych jest używanych. Nawet jeśli 90% tabeli zawiera niezmienne dane, należy je skanować za każdym razem, gdy uruchamiane jest podciśnienie. Podział tabeli może pomóc zredukować tabelę wymagającą odkurzania do mniejszych, zmniejszyć ilość niezmiennych danych do przeskanowania, skrócić ogólny czas odkurzania i uwolnić więcej zasobów systemowych w celu uzyskania dostępu użytkownika, a nie konserwacji systemu.

Jak usuwane są dane, jeśli w ogóle?

Jeśli dane zostaną usunięte zgodnie z harmonogramem, powiedzmy, że dane starsze niż 4 lata zostaną usunięte i zarchiwizowane, może to skutkować ciężkimi deklaracjami usuwania, których uruchomienie może zająć trochę czasu, i jak wspomniano wcześniej, tworzeniem martwych wierszy, które należy odkurzyć. Jeśli zostanie wdrożona dobra strategia partycjonowania, wielogodzinna instrukcja DELETE z późniejszą konserwacją odkurzania może zostać przekształcona w jednominutową instrukcję DROP TABLE na starym stole miesięcznym z zerową konserwacją próżni.

Jak powinien być podzielony stół?

Klucze wzorców dostępu znajdują się w klauzuli WHERE i warunkach JOIN. Za każdym razem, gdy zapytanie określa kolumny w klauzulach WHERE i JOIN, informuje ono bazę danych „to są dane, których chcę”. Podobnie jak w przypadku projektowania indeksów, które są ukierunkowane na te klauzule, strategie partycjonowania polegają na kierowaniu tych kolumn do oddzielania danych i zapewnianiu dostępu do zapytania do jak najmniejszej liczby partycji.

Przykłady:

  1. Tabela transakcji z kolumną daty, która jest zawsze używana w klauzuli where.
  2. Tabela klientów z kolumnami lokalizacji, takimi jak kraj zamieszkania, która jest zawsze używana w klauzulach where.

Najczęstszymi kolumnami, na których należy się skoncentrować przy partycjonowaniu, są zwykle znaczniki czasu, ponieważ zwykle duża część danych to informacje historyczne i prawdopodobnie będą miały dość przewidywalne dane rozłożone na różne grupy czasowe.

Określ rozproszenie danych

Po zidentyfikowaniu kolumn do partycjonowania, powinniśmy przyjrzeć się rozkładowi danych, aby stworzyć rozmiary partycji, które rozkładają dane tak równomiernie, jak to możliwe, na różne partycje podrzędne.

severalnines=# SELECT DATE_TRUNC('year', view_date)::DATE, COUNT(*) FROM website_views GROUP BY 1 ORDER BY 1;
 date_trunc |  count
------------+----------
 2013-01-01 | 11625147
 2014-01-01 | 20819125
 2015-01-01 | 20277739
 2016-01-01 | 20584545
 2017-01-01 | 20777354
 2018-01-01 |   491002
(6 rows)

W tym przykładzie obcinamy kolumnę sygnatury czasowej do tabeli rocznej, co daje około 20 milionów wierszy rocznie. Jeśli wszystkie nasze zapytania określają daty lub zakresy dat, a te określone zwykle obejmują dane w ciągu jednego roku, może to być świetna strategia na początek partycjonowania, ponieważ skutkowałoby to pojedynczą tabelą na rok , z możliwą do opanowania liczbą wierszy na tabelę.

Pobierz oficjalny dokument już dziś Zarządzanie i automatyzacja PostgreSQL za pomocą ClusterControlDowiedz się, co musisz wiedzieć, aby wdrażać, monitorować, zarządzać i skalować PostgreSQLPobierz oficjalny dokument

Tworzenie tabeli partycjonowanej

Istnieje kilka sposobów tworzenia tabel partycjonowanych, jednak skupimy się głównie na najbardziej bogatym w funkcje typie, partycjonowaniu opartym na wyzwalaczach. Wymaga to ręcznej konfiguracji i odrobiny kodowania w proceduralnym języku plpgsql, aby zacząć działać.

Działa poprzez posiadanie tabeli nadrzędnej, która ostatecznie stanie się pusta (lub pozostanie pusta, jeśli jest to nowa tabela) oraz tabel podrzędnych, które DZIEDZICZĄ tabelę nadrzędną. Gdy odpytywana jest tabela nadrzędna, tabele podrzędne są również przeszukiwane pod kątem danych z powodu zastosowania INHERIT do tabel podrzędnych. Jednak ponieważ tabele podrzędne zawierają tylko podzbiory danych rodzica, dodajemy do tabeli OGRANICZENIE, które wykonuje KONTROLĘ i weryfikuje, czy dane pasują do dozwolonych w tabeli. Robi to dwie rzeczy:po pierwsze odrzuca dane, które nie należą, a po drugie informuje planistę zapytań, że w tej tabeli dozwolone są tylko dane pasujące do tego OGRANICZENIA KONTROLI, więc jeśli szukasz danych, które nie pasują do tabeli, nie nawet nie zawracaj sobie głowy wyszukiwaniem.

Na koniec stosujemy wyzwalacz do tabeli nadrzędnej, który wykonuje procedurę składowaną, która decyduje, która tabela podrzędna ma umieścić dane.

Utwórz tabelę

Tworzenie tabeli nadrzędnej jest jak każde inne tworzenie tabeli.

severalnines=# CREATE TABLE data_log (data_log_sid SERIAL PRIMARY KEY,
  date TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW(),
  event_details VARCHAR);
CREATE TABLE

Utwórz tabele podrzędne

Tworzenie tabel podrzędnych jest podobne, ale wiąże się z pewnymi dodatkami. Ze względów organizacyjnych nasze tabele podrzędne będą istniały w osobnym schemacie. Zrób to dla każdej tabeli podrzędnej, odpowiednio zmieniając szczegóły.

UWAGA:Nazwa sekwencji używanej w nextval() pochodzi z sekwencji utworzonej przez rodzica. Jest to kluczowe, aby wszystkie tabele podrzędne używały tej samej sekwencji.

severalnines=# CREATE SCHEMA part;
CREATE SCHEMA

severalnines=# CREATE TABLE part.data_log_2018 (data_log_sid integer DEFAULT nextval('public.data_log_data_log_sid_seq'::regclass),
  date TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW(),
  event_details VARCHAR)
 INHERITS (public.data_log);
CREATE TABLE

severalnines=# ALTER TABLE ONLY part.data_log_2018
    ADD CONSTRAINT data_log_2018_pkey PRIMARY KEY (data_log_sid);
ALTER TABLE

severalnines=# ALTER TABLE part.data_log_2018 ADD CONSTRAINT data_log_2018_date CHECK (date >= '2018-01-01' AND date < '2019-01-01');
ALTER TABLE

Utwórz funkcję i wyzwalacz

Na koniec tworzymy naszą procedurę składowaną i dodajemy wyzwalacz do naszej tabeli nadrzędnej.

severalnines=# CREATE OR REPLACE FUNCTION 
 public.insert_trigger_table()
  RETURNS trigger
  LANGUAGE plpgsql
 AS $function$
 BEGIN
     IF NEW.date >= '2018-01-01' AND NEW.date < '2019-01-01' THEN
         INSERT INTO part.data_log_2018 VALUES (NEW.*);
         RETURN NULL;
     ELSIF NEW.date >= '2019-01-01' AND NEW.date < '2020-01-01' THEN
         INSERT INTO part.data_log_2019 VALUES (NEW.*);
         RETURN NULL;
     END IF;
 END;
 $function$;
CREATE FUNCTION

severalnines=# CREATE TRIGGER insert_trigger BEFORE INSERT ON data_log FOR EACH ROW EXECUTE PROCEDURE insert_trigger_table();
CREATE TRIGGER

Przetestuj

Teraz, gdy wszystko jest już stworzone, przetestujmy to. W tym teście dodałem więcej rocznych tabel obejmujących lata 2013-2020.

Uwaga:odpowiedź wstawiania poniżej to „WSTAW 0 0”, co sugerowałoby, że niczego nie wstawiono. Zostanie to omówione w dalszej części tego artykułu.

severalnines=# INSERT INTO data_log (date, event_details) VALUES ('2018-08-20 15:22:14', 'First insert');
INSERT 0 0

severalnines=# SELECT * FROM data_log WHERE date >= '2018-08-01' AND date < '2018-09-01';
 data_log_sid |            date            | event_details
--------------+----------------------------+---------------
            1 | 2018-08-17 23:01:38.324056 | First insert
(1 row)

Istnieje, ale spójrzmy na planer zapytań, aby upewnić się, że wiersz pochodzi z właściwej tabeli podrzędnej, a tabela nadrzędna w ogóle nie zwróciła żadnych wierszy.

severalnines=# EXPLAIN ANALYZE SELECT * FROM data_log;
                                                    QUERY PLAN
------------------------------------------------------------------------------------------------------------------
 Append  (cost=0.00..130.12 rows=5813 width=44) (actual time=0.016..0.019 rows=1 loops=1)
   ->  Seq Scan on data_log  (cost=0.00..1.00 rows=1 width=44) (actual time=0.007..0.007 rows=0 loops=1)
   ->  Seq Scan on data_log_2015  (cost=0.00..21.30 rows=1130 width=44) (actual time=0.001..0.001 rows=0 loops=1)
   ->  Seq Scan on data_log_2013  (cost=0.00..17.80 rows=780 width=44) (actual time=0.001..0.001 rows=0 loops=1)
   ->  Seq Scan on data_log_2014  (cost=0.00..17.80 rows=780 width=44) (actual time=0.001..0.001 rows=0 loops=1)
   ->  Seq Scan on data_log_2016  (cost=0.00..17.80 rows=780 width=44) (actual time=0.001..0.001 rows=0 loops=1)
   ->  Seq Scan on data_log_2017  (cost=0.00..17.80 rows=780 width=44) (actual time=0.001..0.001 rows=0 loops=1)
   ->  Seq Scan on data_log_2018  (cost=0.00..1.02 rows=2 width=44) (actual time=0.005..0.005 rows=1 loops=1)
   ->  Seq Scan on data_log_2019  (cost=0.00..17.80 rows=780 width=44) (actual time=0.001..0.001 rows=0 loops=1)
   ->  Seq Scan on data_log_2020  (cost=0.00..17.80 rows=780 width=44) (actual time=0.001..0.001 rows=0 loops=1)
 Planning time: 0.373 ms
 Execution time: 0.069 ms
(12 rows)

Dobra wiadomość, wstawiony przez nas pojedynczy wiersz wylądował w tabeli 2018, gdzie należy. Ale jak widzimy, zapytanie nie określa klauzuli WHERE przy użyciu kolumny daty, więc aby pobrać wszystko, planer zapytań i wykonanie wykonały sekwencyjne skanowanie każdej tabeli.

Następnie przetestujmy za pomocą klauzuli where.

severalnines=# EXPLAIN ANALYZE SELECT * FROM data_log WHERE date >= '2018-08-01' AND date < '2018-09-01';
                                                                   QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------
 Append  (cost=0.00..2.03 rows=2 width=44) (actual time=0.013..0.014 rows=1 loops=1)
   ->  Seq Scan on data_log  (cost=0.00..1.00 rows=1 width=44) (actual time=0.007..0.007 rows=0 loops=1)
         Filter: ((date >= '2018-08-01 00:00:00'::timestamp without time zone) AND (date < '2018-09-01 00:00:00'::timestamp without time zone))
   ->  Seq Scan on data_log_2018  (cost=0.00..1.03 rows=1 width=44) (actual time=0.006..0.006 rows=1 loops=1)
         Filter: ((date >= '2018-08-01 00:00:00'::timestamp without time zone) AND (date < '2018-09-01 00:00:00'::timestamp without time zone))
 Planning time: 0.591 ms
 Execution time: 0.041 ms
(7 rows)

Tutaj widzimy, że planowanie i wykonanie zapytań wykonało sekwencyjne skanowanie dwóch tabel, tabeli nadrzędnej i tabeli podrzędnej dla 2018. Istnieją tabele podrzędne dla lat 2013-2020, ale te inne niż 2018 nigdy nie były dostępne, ponieważ klauzula where ma zakres należący tylko do 2018. Planer zapytań wykluczył wszystkie inne tabele, ponieważ CHECK CONSTRAINT uważa, że ​​dane nie mogą istnieć w tych tabelach.

Praca na partycjach za pomocą ścisłych narzędzi ORM lub walidacji wstawionych wierszy

Jak wspomniano wcześniej, zbudowany przez nas przykład zwraca „WSTAW 0 0”, mimo że wstawiliśmy wiersz. Jeśli aplikacje wstawiające dane do tych tabel partycjonowanych polegają na sprawdzeniu, czy wstawione wiersze są poprawne, zakończy się to niepowodzeniem. Jest poprawka, ale dodaje kolejną warstwę złożoności do partycjonowanej tabeli, więc można ją zignorować, jeśli ten scenariusz nie stanowi problemu dla aplikacji korzystających z partycjonowanej tabeli.

Używanie widoku zamiast tabeli nadrzędnej.

Rozwiązaniem tego problemu jest utworzenie widoku, który wysyła zapytania do tabeli nadrzędnej i bezpośrednie instrukcje INSERT do widoku. Wstawianie do widoku może wydawać się szalone, ale właśnie w tym miejscu pojawia się wyzwalacz widoku.

severalnines=# CREATE VIEW data_log_view AS 
 SELECT data_log.data_log_sid,
     data_log.date,
     data_log.event_details
    FROM data_log;
CREATE VIEW

severalnines=# ALTER VIEW data_log_view ALTER COLUMN data_log_sid SET default nextval('data_log_data_log_sid_seq'::regclass);
ALTER VIEW

Wykonywanie zapytań w tym widoku będzie wyglądać tak samo, jak zapytania dotyczące głównej tabeli, a klauzule WHERE oraz JOINS będą działać zgodnie z oczekiwaniami.

Wyświetl określoną funkcję i wyzwalacz

Zamiast korzystać z funkcji i wyzwalacza, które zdefiniowaliśmy wcześniej, oba będą nieco inne. Zmiany pogrubioną czcionką.

CREATE OR REPLACE FUNCTION public.insert_trigger_view()
 RETURNS trigger
 LANGUAGE plpgsql
AS $function$
BEGIN
    IF NEW.date >= '2018-01-01' AND NEW.date < '2019-01-01' THEN
        INSERT INTO part.data_log_2018 VALUES (NEW.*);
        RETURN NEW;

    ELSIF NEW.date >= '2019-01-01' AND NEW.date < '2020-01-01' THEN
        INSERT INTO part.data_log_2019 VALUES (NEW.*);
        RETURN NEW;

    END IF;
END;
$function$;

severalnines=# CREATE TRIGGER insert_trigger INSTEAD OF INSERT ON data_log_view FOR EACH ROW EXECUTE PROCEDURE insert_trigger_view();

Definicja „ZAMIAST” przejmuje polecenie wstawiania w widoku (co i tak by nie zadziałało) i zamiast tego wykonuje funkcję. Zdefiniowana przez nas funkcja ma bardzo konkretny wymóg wykonania „RETURN NEW” po zakończeniu wstawiania do tabel podrzędnych. Bez tego (lub zrobienie tego tak, jak zrobiliśmy to wcześniej z „RETURN NULL”) spowoduje to „WSTAW 0 0” zamiast „WSTAW 0 1”, jak byśmy się spodziewali.

Przykład:

severalnines=# INSERT INTO data_log_view (date, event_details) VALUES ('2018-08-20 18:12:48', 'First insert on the view');
INSERT 0 1

severalnines=# EXPLAIN ANALYZE SELECT * FROM data_log_view WHERE date >= '2018-08-01' AND date < '2018-09-01';
                                                                   QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------
 Append  (cost=0.00..2.03 rows=2 width=44) (actual time=0.015..0.017 rows=2 loops=1)
   ->  Seq Scan on data_log  (cost=0.00..1.00 rows=1 width=44) (actual time=0.009..0.009 rows=0 loops=1)
         Filter: ((date >= '2018-08-01 00:00:00'::timestamp without time zone) AND (date < '2018-09-01 00:00:00'::timestamp without time zone))
   ->  Seq Scan on data_log_2018  (cost=0.00..1.03 rows=1 width=44) (actual time=0.006..0.007 rows=2 loops=1)
         Filter: ((date >= '2018-08-01 00:00:00'::timestamp without time zone) AND (date < '2018-09-01 00:00:00'::timestamp without time zone))
 Planning time: 0.633 ms
 Execution time: 0.048 ms
(7 rows)

severalnines=# SELECT * FROM data_log_view WHERE date >= '2018-08-01' AND date < '2018-09-01';
 data_log_sid |        date         |      event_details
--------------+---------------------+--------------------------
            1 | 2018-08-20 15:22:14 | First insert
            2 | 2018-08-20 18:12:48 | First insert on the view
(2 rows)

Aplikacje testujące pod kątem wstawionej wartości „liczba wierszy” pod kątem poprawności sprawią, że ta poprawka będzie działać zgodnie z oczekiwaniami. W tym przykładzie dodaliśmy _view do naszego widoku i procedury składowanej, ale jeśli chcemy, aby tabela została podzielona na partycje bez wiedzy użytkowników / zmiany aplikacji, zmienilibyśmy nazwę tabeli nadrzędnej na data_log_parent i wywołalibyśmy widok według starego nazwa tabeli nadrzędnej.

Aktualizacja wiersza i zmiana wartości partycjonowanej kolumny

Należy pamiętać, że jeśli wykonanie aktualizacji danych w tabeli partycjonowanej i zmiana wartości kolumny na coś, co nie jest dozwolone przez ograniczenie, spowoduje błąd. Jeśli tego typu aktualizacja nigdy nie nastąpi, można ją zignorować, ale jeśli jest taka możliwość, należy napisać nowy wyzwalacz dla procesów UPDATE, który skutecznie usunie wiersz ze starej partycji podrzędnej i wstawi nowy do partycji nowa docelowa partycja podrzędna.

Tworzenie przyszłych partycji

Tworzenie przyszłych partycji można wykonać na kilka różnych sposobów, każdy z ich zaletami i wadami.

Przyszły twórca partycji

Zewnętrzny program można napisać, aby utworzyć przyszłe partycje X razy, zanim będą potrzebne. W przykładzie partycjonowania podzielonym na dzień, następna potrzebna partycja do utworzenia (w naszym przypadku 2019) może zostać utworzona w grudniu. Może to być skrypt ręczny uruchamiany przez administratora bazy danych lub ustawiony tak, aby cron uruchamiał go w razie potrzeby. Roczne partycje oznaczają, że działa raz w roku, jednak partycje dzienne są powszechne, a codzienne zadanie cron zapewnia szczęśliwsze DBA.

Automatyczny twórca partycji

Dzięki potędze plpgsql możemy przechwycić błędy, jeśli próbujemy wstawić dane do partycji podrzędnej, która nie istnieje, i w locie utworzyć potrzebną partycję, a następnie spróbować wstawić ponownie. Ta opcja działa dobrze, z wyjątkiem sytuacji, gdy wielu różnych klientów wstawiających podobne dane w tym samym czasie może spowodować sytuację wyścigu, w której jeden klient tworzy tabelę, podczas gdy inny próbuje utworzyć tę samą tabelę i otrzymuje błąd, że już istnieje. Sprytne i zaawansowane programowanie plpgsql może to naprawić, ale czy jest to warte wysiłku, jest przedmiotem dyskusji. Jeśli ten stan wyścigu nie nastąpi z powodu wzorców wstawiania, nie ma się czym martwić.

Upuszczanie partycji

Jeśli reguły przechowywania danych nakazują usuwanie danych po określonym czasie, staje się to łatwiejsze w przypadku tabel podzielonych na partycje, jeśli są one podzielone na partycje według kolumny daty. Jeśli mamy usunąć dane, które mają 10 lat, może to być tak proste, jak:

severalnines=# DROP TABLE part.data_log_2007;
DROP TABLE

Jest to znacznie szybsze i bardziej wydajne niż polecenie „DELETE”, ponieważ nie powoduje żadnych martwych krotek, które należy wyczyścić za pomocą odkurzacza.

Uwaga:jeśli usuwasz tabele z konfiguracji partycji, kod w funkcjach wyzwalacza również powinien zostać zmieniony, aby nie kierować daty do usuniętej tabeli.

Co warto wiedzieć przed partycjonowaniem

Tabele partycjonowania mogą zapewnić drastyczną poprawę wydajności, ale może też ją pogorszyć. Przed przekazaniem na serwery produkcyjne strategia partycjonowania powinna zostać gruntownie przetestowana pod kątem spójności danych, szybkości działania, wszystkiego. Partycjonowanie stołu ma kilka ruchomych części, wszystkie należy przetestować, aby upewnić się, że nie ma żadnych problemów.

Jeśli chodzi o decydowanie o liczbie partycji, zdecydowanie zaleca się, aby liczba tabel podrzędnych była mniejsza niż 1000, a nawet mniejsza, jeśli to możliwe. Gdy liczba tabel podrzędnych przekroczy ~1000, wydajność zaczyna spadać, ponieważ sam planer zapytań zajmuje znacznie więcej czasu tylko na wykonanie planu zapytania. Często zdarza się, że plan zapytania zajmuje wiele sekund, podczas gdy faktyczne wykonanie zajmuje tylko kilka milisekund. W przypadku obsługi tysięcy zapytań na minutę, kilka sekund może spowodować zatrzymanie aplikacji.

Procedury składowane wyzwalacza plpgsql mogą również stać się skomplikowane, a jeśli są zbyt skomplikowane, również spowolnić działanie. Procedura składowana jest wykonywana raz dla każdego wiersza wstawionego do tabeli. Jeśli skończy się to zbyt dużym przetwarzaniem dla każdego wiersza, wstawianie może stać się zbyt wolne. Testy wydajności upewnią się, że nadal jest w akceptowalnym zakresie.

Zdobądź kreatywność

Tabele partycjonowania w PostgreSQL mogą być tak zaawansowane, jak to konieczne. Zamiast kolumn dat tabele można podzielić na kolumny „kraj”, z tabelą dla każdego kraju. Partycjonowanie można przeprowadzić w wielu kolumnach, takich jak kolumna „data” i „kraj”. To sprawi, że procedura składowana obsługująca wstawki będzie bardziej złożona, ale jest to w 100% możliwe.

Pamiętaj, że celem partycjonowania jest rozbicie bardzo dużych tabel na mniejsze i zrobienie tego w dobrze przemyślany sposób, aby umożliwić planerowi zapytań dostęp do danych szybciej niż w większej oryginalnej tabeli.

Deklaratywne partycjonowanie

W PostgreSQL 10 i nowszych wprowadzono nową funkcję partycjonowania „Deklaratywne partycjonowanie”. Jest to łatwiejszy sposób na skonfigurowanie partycji, jednak ma pewne ograniczenia. Jeśli ograniczenia są akceptowalne, prawdopodobnie będzie działać szybciej niż ręczna konfiguracja partycji, ale liczne testy to zweryfikują.

Oficjalna dokumentacja postgresql zawiera informacje na temat partycjonowania deklaratywnego i sposobu jego działania. Jest to nowość w PostgreSQL 10, a wraz z pojawieniem się na horyzoncie 11 wersji PostgreSQL, niektóre ograniczenia zostały naprawione, ale nie wszystkie. W miarę rozwoju PostgreSQL partycjonowanie deklaratywne może stać się pełnym zamiennikiem bardziej złożonego partycjonowania opisanego w tym artykule. Do tego czasu partycjonowanie deklaratywne może być łatwiejszą alternatywą, jeśli żadne z ograniczeń nie ogranicza potrzeb partycjonowania.

Deklaratywne ograniczenia partycjonowania

Dokumentacja PostgreSQL opisuje wszystkie ograniczenia tego typu partycjonowania w PostgreSQL 10, ale świetny przegląd można znaleźć na Oficjalnej Wiki PostgreSQL, która zawiera listę ograniczeń w łatwiejszym do odczytania formacie, a także wskazuje, które z nich zostały naprawione w nadchodzący PostgreSQL 11.

Zapytaj społeczność

Administratorzy baz danych na całym świecie od dawna projektują zaawansowane i niestandardowe strategie partycjonowania, a wielu z nas spędza czas na IRC i listach mailingowych. Jeśli potrzebna jest pomoc w podjęciu decyzji o najlepszej strategii lub po prostu w naprawieniu błędu w procedurze składowanej, społeczność jest tutaj, aby pomóc.

  • IRC
    Freenode ma bardzo aktywny kanał o nazwie #postgres, w którym użytkownicy pomagają sobie nawzajem w zrozumieniu koncepcji, naprawianiu błędów lub znajdowaniu innych zasobów.
  • Listy mailingowe
    PostgreSQL ma kilka list dyskusyjnych, do których można dołączyć. Pytania/problemy o dłuższym formularzu można wysyłać tutaj i mogą one dotrzeć do znacznie większej liczby osób niż IRC w danym momencie. Listy można znaleźć na stronie PostgreSQL, a listy pgsql-general lub pgsql-admin są dobrymi zasobami.

  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Czy można wyłączyć przetwarzanie ofert w poleceniu Postgres COPY w formacie CSV?

  2. jak emulować ignorowanie wstawiania i aktualizację zduplikowanych kluczy (sql merge) za pomocą postgresql?

  3. Jak działa Tanh() w PostgreSQL

  4. Czy gwarantowane jest zachowanie porządku w podzapytaniu?

  5. Funkcja usuwania akcentów w postgreSQL