Partycjonowanie i sharding w PostgreSQL: podstawy

Kacper Sieradziński
Kacper Sieradziński
26 września 2025PostgreSQL4 min czytania

Każda baza rośnie, aż w końcu zaczyna spowalniać. Zapytania, które kiedyś działały w milisekundach, nagle potrzebują sekund. VACUUM trwa wieczność, a raport miesięczny ciągnie gigabajty danych. To właśnie moment, w którym warto sięgnąć po partycjonowanie – czyli logiczny podział dużej tabeli na mniejsze, łatwiejsze w obsłudze fragmenty.

Obraz główny Partycjonowanie i sharding w PostgreSQL: podstawy

Zapisz się na nasz newsletter

Otrzymuj regularne aktualizacje, specjalne oferty i porady od ekspertów, które pomogą Ci osiągnąć więcej w krótszym czasie.

W tym artykule zobaczysz, jak działa partycjonowanie w PostgreSQL, jakie są jego typy (RANGE, LIST, HASH), kiedy faktycznie daje zysk i jak uniknąć pułapek związanych z planowaniem i indeksami. Na koniec przyjrzymy się też shardingowi — krokowi dalej, gdy pojedynczy serwer to już za mało.

Czym jest partycjonowanie danych

Partycjonowanie danych to logiczny podział jednej tabeli na wiele mniejszych tabel-partycji zarządzanych przez Postgresa jako rozproszone segmenty jednej relacji. Daje to szybsze skany dzięki partition pruning, mniejsze blokady, tańszy VACUUM i łatwiejszą retencję (DROP/DETACH partycji zamiast masowych DELETE). Partycjonowanie tabel nie zmienia modelu logicznego – aplikacja dalej widzi jedną tabelę, a silnik decyduje, które partycje przeszukać i gdzie wstawić wiersz.

W PostgreSQL partycjonowanie jest deklaratywne (PARTITION BY). Wybór klucza partycjonowania powinien odzwierciedlać wzorce zapytań i ładowania danych, aby maksymalnie wykorzystać routing zapytań do partycji i minimalizować I/O. Jeśli dopiero zaczynasz z bazą, zobacz: PostgreSQL: wprowadzenie i instalacja.

Partycjonowanie zakresowe, listowe i hash

Partycjonowanie zakresowe (RANGE) dzieli dane po przedziałach, np. data lub liczby. To najczęstszy wybór dla szeregów czasowych i retencji, bo pozwala szybko usuwać stare okresy i skutecznie wykorzystuje partition pruning w zapytaniach z warunkami na czas. Partycjonowanie listowe (LIST) mapuje dyskretne wartości na partycje, np. region, status, tenant_id z krótkiej listy. Sprawdza się, gdy filtry zwykle wskazują konkretną kategorię. Partycjonowanie hash (HASH) rozkłada wiersze równomiernie po partycjach na podstawie hasha klucza; jest dobre do równoważenia obciążenia, gdy wartości są liczne i brak naturalnych zakresów.

Wybór klucza partycjonowania jest krytyczny. Powinien pojawiać się w WHERE/JOIN, aby pruning był maksymalny, a indeksy lokalne trafne. Dla zakresów czasowych często łączy się RANGE po dacie z indeksami po kolumnach zapytań. Dobór indeksów na partycjach zależy od planów zapytań – przydatny przegląd typów znajdziesz w: Indeksy w PostgreSQL: B-Tree, Hash, GIN, GiST i BRIN.

MySQL — Jak zacząć? Darmowy e-book

MySQL — Jak zacząć? Darmowy e-book

Praktyczny przewodnik po świecie SQL. Poznaj typy danych, zapytania SELECT, JOIN, funkcje agregujące i nie tylko.

Tworzenie partycji krok po kroku

Poniżej minimalny przykład tabeli zamówień partycjonowanej zakresowo po dacie. Klucz główny zawiera kolumnę partycjonowania, co pozwala tworzyć unikalne ograniczenia per tabela-partition bez naruszania spójności.

SQL
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 -- 1) Tabela nadrzędna (deklaratywne partycjonowanie) CREATE TABLE orders ( id bigint GENERATED ALWAYS AS IDENTITY, customer_id bigint NOT NULL, order_date date NOT NULL, amount numeric(12,2) NOT NULL, status text NOT NULL, PRIMARY KEY (id, order_date) -- PK musi zawierać klucz partycjonowania ) PARTITION BY RANGE (order_date); -- 2) Partycje miesięczne + partycja domyślna (na wypadek luk) CREATE TABLE orders_2025_10 PARTITION OF orders FOR VALUES FROM ('2025-10-01') TO ('2025-11-01'); CREATE TABLE orders_2025_11 PARTITION OF orders FOR VALUES FROM ('2025-11-01') TO ('2025-12-01'); CREATE TABLE orders_default PARTITION OF orders DEFAULT; -- 3) Indeksy lokalne (tworzone per partycja wg potrzeb zapytań) CREATE INDEX ON orders_2025_10 (customer_id, order_date); CREATE INDEX ON orders_2025_11 (customer_id, order_date); -- 4) Wstawianie i pruning INSERT INTO orders (customer_id, order_date, amount, status) VALUES (42, '2025-10-10', 99.90, 'paid'); EXPLAIN SELECT sum(amount) FROM orders WHERE order_date >= date '2025-10-01' AND order_date < date '2025-11-01';

W produkcji automatyzuj tworzenie nowych partycji (np. job miesięczny) i okresowo odłączaj stare (DETACH PARTITION) lub usuwaj całe zakresy (DROP PARTITION), co skraca maintenance. Sprawdzaj plany wykonania, by upewnić się, że partition pruning działa (warunki muszą być sargowalne); praktyczne wskazówki znajdziesz w: Optymalizacja zapytań SQL i plany wykonania.

Sharding – skalowanie poziome

Sharding w SQL to rozbicie danych jednej logicznej tabeli na wiele węzłów (rozproszone tabele). To skalowanie poziome baz danych poza pojedynczy serwer. W PostgreSQL osiąga się to najczęściej przez partycjonowanie + FDW (postgres_fdw) i routing zapytań w aplikacji, lub przez rozszerzenia klastra (np. Citus). Kosztem jest złożoność: brak globalnych transakcji o niskim koszcie, droższe JOIN między shardami i konieczność rebalansowania.

Strategia shardingu zwykle używa hash po tenant_id (równy rozkład), zakresu po czasie (łatwa archiwizacja) lub listy regionów. Klucz shardingu powinien minimalizować cross-shard JOIN/AGG, a aplikacja lub middleware musi wiedzieć, gdzie kierować zapytania i zapisy. Indeksacja dalej ma znaczenie per shard – podstawy doboru indeksów przypomni: Indeksy w SQL: teoria i praktyka.

Kiedy warto zastosować partycjonowanie

Rozważ partycjonowanie tabel, gdy rozmiar rośnie do dziesiątek lub setek gigabajtów, a zapytania naturalnie filtrują po jednej kolumnie (zwykle czasie). Jeśli realizujesz retencję przez masowe DELETE i borykasz się z bloatem oraz długim VACUUM, partycje pozwalają zastąpić to natychmiastowym DROP/DETACH. Gdy okno robocze zapytań obejmuje tylko ostatnie N dni, zakresowe partycjonowanie danych zmniejsza skan I/O i pamięć planisty.

W sharding w SQL wchodź dopiero, gdy pojedynczy serwer (CPU/IO/RAM) i pojedyncza replika nie wystarczają, a pionowa rozbudowa jest nieopłacalna lub niemożliwa. Wcześniej wykorzystaj replikację do odczytów, dobre indeksy, kompresję, przegląd planów, a także właściwe partycjonowanie danych, które często usuwa wąskie gardła bez rozpraszania bazy.

Typowe problemy i dobre praktyki

Najczęstsze pułapki partycjonowania: zbyt wiele małych partycji (koszt planowania, katalog), brak partycji domyślnej powodujący błędy INSERT, unikalne klucze nieobejmujące kolumny partycjonowania, luki w zakresach, niespójne indeksy między partycjami i brak ANALYZE po masowym ładowaniu. W wersjach PG globalne ograniczenia unikalności nie istnieją – unikalność musi zawierać klucz partycjonowania, inaczej nie będzie egzekwowana.

Dobre praktyki: wybierz klucz partycjonowania zgodny z filtrami i joinami; utrzymuj stały rozmiar partycji (np. miesiąc/tydzień) i harmonogram tworzenia/archiwizacji; dodaj partycję domyślną jako bezpiecznik; utrzymuj spójny zestaw indeksów per partycja; monitoruj partition pruning w planach; wykonuj regularny maintenance partycji (VACUUM/ANALYZE/REINDEX tam, gdzie trzeba); przy reorganizacji używaj DETACH/ATTACH, aby ograniczyć przestoje. Jeśli interesuje Cię wpływ struktur danych na plany i I/O, zobacz: Optymalizacja zapytań SQL i plany wykonania.

Kurs SkumajBazy — Czas w końcu nauczyć się SQLa

Kurs SkumajBazy — Czas w końcu nauczyć się SQLa

Kompleksowy kurs SQL dla programistów, analityków i wszystkich, którzy chcą efektywnie pracować z danymi. Od podstaw do zaawansowanych zapytań.