PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Una guida al partizionamento dei dati in PostgreSQL

Cos'è il partizionamento dei dati?

Per i database con tabelle estremamente grandi, il partizionamento è un trucco meraviglioso e astuto per i progettisti di database per migliorare le prestazioni del database e rendere la manutenzione molto più semplice. La dimensione massima consentita della tabella in un database PostgreSQL è 32 TB, tuttavia, a meno che non sia in esecuzione su un computer non ancora inventato dal futuro, potrebbero sorgere problemi di prestazioni su una tabella con solo un centesimo di quello spazio.

Il partizionamento divide una tabella in più tabelle e generalmente viene eseguita in modo che le applicazioni che accedono alla tabella non notano alcuna differenza, oltre a essere più veloci nell'accedere ai dati di cui ha bisogno. Suddividendo la tabella in più tabelle, l'idea è di consentire all'esecuzione delle query di dover scansionare tabelle e indici molto più piccoli per trovare i dati necessari. Indipendentemente dall'efficienza di una strategia di indicizzazione, la scansione di un indice per una tabella da 50 GB sarà sempre molto più veloce di un indice per una tabella da 500 GB. Questo vale anche per le scansioni di tabelle, perché a volte le scansioni di tabelle sono semplicemente inevitabili.

Quando si introduce una tabella partizionata nel pianificatore di query, ci sono alcune cose da sapere e comprendere sul pianificatore di query stesso. Prima che qualsiasi query venga effettivamente eseguita, il pianificatore di query prenderà la query e pianificherà il modo più efficiente per accedere ai dati. Suddividendo i dati in diverse tabelle, il pianificatore può decidere a quali tabelle accedere e quali ignorare completamente, in base al contenuto di ciascuna tabella.

Questo viene fatto aggiungendo vincoli alle tabelle suddivise che definiscono quali dati sono consentiti in ciascuna tabella e, con una buona progettazione, possiamo fare in modo che il pianificatore di query esegua la scansione di un piccolo sottoinsieme di dati anziché dell'intera cosa.

Una tabella dovrebbe essere partizionata?

Il partizionamento può migliorare drasticamente le prestazioni di un tavolo se eseguito correttamente, ma se eseguito in modo errato o quando non necessario, può peggiorare le prestazioni, persino inutilizzabili.

Quanto è grande il tavolo?

Non esiste una vera regola fissa per quanto grande deve essere una tabella prima che il partizionamento sia un'opzione, ma in base alle tendenze di accesso al database, gli utenti e gli amministratori del database inizieranno a vedere le prestazioni su una tabella specifica iniziare a peggiorare man mano che diventa più grande. In generale, il partizionamento dovrebbe essere considerato solo quando qualcuno dice "Non posso fare X perché la tabella è troppo grande". Per alcuni host, 200 GB potrebbero essere il momento giusto per partizionare, per altri potrebbe essere il momento di partizionare quando raggiunge 1 TB.

Se si ritiene che il tavolo sia "troppo grande", è il momento di esaminare i modelli di accesso. Conoscendo le applicazioni che accedono al database o monitorando i log e generando report di query con qualcosa come pgBadger, possiamo vedere come si accede a una tabella e, a seconda di come si accede, possiamo avere opzioni per una buona strategia di partizionamento.

Per ulteriori informazioni su pgBadger e su come utilizzarlo, consulta il nostro precedente articolo su pgBadger.

Il rigonfiamento del tavolo è un problema?

Le righe aggiornate ed eliminate generano tuple morte che alla fine devono essere ripulite. I tavoli aspiranti, sia manualmente che automaticamente, esaminano ogni riga della tabella e determina se devono essere recuperati o lasciati soli. Più grande è la tabella, più tempo richiede questo processo e più risorse di sistema utilizzate. Anche se il 90% di una tabella non modifica i dati, è necessario scansionarla ogni volta che si esegue un'aspirazione. La partizione della tabella può aiutare a ridurre la tabella che deve essere aspirata a quelle più piccole, riducendo la quantità di dati immutabili che devono essere scansionati, meno tempo per passare l'aspirapolvere in generale e più risorse di sistema liberate per l'accesso dell'utente piuttosto che per la manutenzione del sistema.

Come vengono eliminati i dati, se non del tutto?

Se i dati vengono eliminati in base a una pianificazione, ad esempio i dati più vecchi di 4 anni vengono eliminati e archiviati, ciò potrebbe comportare pesanti dichiarazioni di eliminazione che possono richiedere tempo per l'esecuzione e, come accennato in precedenza, la creazione di righe morte che devono essere aspirate. Se viene implementata una buona strategia di partizionamento, un'istruzione DELETE di più ore con successiva manutenzione del vuoto potrebbe essere trasformata in un'istruzione DROP TABLE di un minuto su una vecchia tabella mensile senza manutenzione del vuoto.

Come deve essere partizionata la tabella?

Le chiavi per i modelli di accesso si trovano nella clausola WHERE e nelle condizioni JOIN. Ogni volta che una query specifica colonne nelle clausole WHERE e JOIN, dice al database "questi sono i dati che voglio". Proprio come la progettazione di indici che prendono di mira queste clausole, le strategie di partizionamento si basano sul targeting di queste colonne per separare i dati e consentire alla query di accedere al minor numero possibile di partizioni.

Esempi:

  1. Una tabella delle transazioni, con una colonna della data che viene sempre utilizzata in una clausola where.
  2. Una tabella dei clienti con colonne di località, ad esempio il paese di residenza che viene sempre utilizzato nelle clausole where.

Le colonne più comuni su cui concentrarsi per il partizionamento sono in genere timestamp, poiché di solito un'enorme quantità di dati è costituita da informazioni storiche e probabilmente avrà dati piuttosto prevedibili distribuiti in diversi raggruppamenti temporali.

Determina la diffusione dei dati

Una volta identificate le colonne su cui partizionare, dovremmo dare un'occhiata alla diffusione dei dati, con l'obiettivo di creare dimensioni delle partizioni che diffondano i dati il ​​più uniformemente possibile tra le diverse partizioni figlio.

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)

In questo esempio, tronchiamo la colonna timestamp in una tabella annuale, ottenendo circa 20 milioni di righe all'anno. Se tutte le nostre query specificano una o più date o intervalli di date e quelle specificate di solito coprono i dati entro un solo anno, questa potrebbe essere un'ottima strategia di partenza per il partizionamento, poiché risulterebbe in una singola tabella all'anno , con un numero gestibile di righe per tabella.

Scarica il whitepaper oggi Gestione e automazione di PostgreSQL con ClusterControlScopri cosa devi sapere per distribuire, monitorare, gestire e ridimensionare PostgreSQLScarica il whitepaper

Creazione di una tabella partizionata

Esistono un paio di modi per creare tabelle partizionate, tuttavia ci concentreremo principalmente sul tipo più ricco di funzionalità disponibile, il partizionamento basato su trigger. Ciò richiede una configurazione manuale e un po' di codifica nel linguaggio procedurale plpgsql per funzionare.

Funziona avendo una tabella padre che alla fine diventerà vuota (o rimarrà vuota se si tratta di una nuova tabella) e tabelle figlio che EREDITANO la tabella padre. Quando viene eseguita una query sulla tabella padre, vengono ricercati anche i dati nelle tabelle figlio a causa di INHERIT applicato alle tabelle figlio. Tuttavia, poiché le tabelle figlie contengono solo sottoinsiemi dei dati del genitore, aggiungiamo un CONSTRAINT sulla tabella che esegue un CHECK e verifica che i dati corrispondano a quanto consentito nella tabella. Questo fa due cose:in primo luogo rifiuta i dati che non appartengono e in secondo luogo dice al pianificatore di query che solo i dati che corrispondono a questo VINCOLO CONTROLLO sono consentiti in questa tabella, quindi se si cercano dati che non corrispondono alla tabella, non Non preoccuparti nemmeno di cercarlo.

Infine, applichiamo un trigger alla tabella padre che esegue una procedura memorizzata che decide in quale tabella figlio inserire i dati.

Crea tabella

La creazione della tabella padre è come qualsiasi altra creazione di tabelle.

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

Crea tabelle figlio

La creazione delle tabelle figlio è simile, ma comporta alcune aggiunte. Per motivi organizzativi, avremo le nostre tabelle figlie in uno schema separato. Fallo per ogni tabella figlio, modificando di conseguenza i dettagli.

NOTA:il nome della sequenza utilizzata in nextval() deriva dalla sequenza creata dal genitore. Questo è fondamentale affinché tutte le tabelle figlie utilizzino la stessa sequenza.

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

Crea funzione e trigger

Infine, creiamo la nostra stored procedure e aggiungiamo il trigger alla nostra tabella padre.

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

Prova

Ora che è tutto creato, proviamolo. In questo test ho aggiunto più tabelle annuali che coprono il periodo 2013-2020.

Nota:la risposta di inserimento di seguito è "INSERT 0 0", il che suggerisce che non ha inserito nulla. Questo sarà affrontato più avanti in questo articolo.

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)

Esiste, ma esaminiamo il pianificatore di query per assicurarci che la riga provenga dalla tabella figlio corretta e che la tabella padre non abbia restituito alcuna riga.

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)

Buone notizie, la singola riga che abbiamo inserito è arrivata nella tabella 2018, a cui appartiene. Ma come possiamo vedere, la query non specifica una clausola where utilizzando la colonna della data, quindi per recuperare tutto, il pianificatore di query e l'esecuzione hanno eseguito una scansione sequenziale su ogni singola tabella.

Quindi, testiamo utilizzando una clausola 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)

Qui possiamo vedere che il pianificatore di query e l'esecuzione hanno eseguito una scansione sequenziale su due tabelle, la tabella padre e la tabella figlio per il 2018. Esistono tabelle figlio per gli anni 2013 - 2020, ma a quelle diverse dal 2018 non è mai stato possibile accedere perché la clausola where ha un intervallo che appartiene solo al 2018. Il pianificatore di query ha escluso tutte le altre tabelle perché CHECK CONSTRAINT ritiene impossibile che i dati esistano in quelle tabelle.

Partizioni funzionanti con strumenti ORM rigorosi o convalida riga inserita

Come accennato in precedenza, l'esempio che abbiamo costruito restituisce un "INSERT 0 0" anche se abbiamo inserito una riga. Se le applicazioni che inseriscono dati in queste tabelle partizionate si basano sulla verifica che le righe inserite siano corrette, queste avranno esito negativo. C'è una soluzione, ma aggiunge un altro livello di complessità alla tabella partizionata, quindi può essere ignorata se questo scenario non è un problema per le applicazioni che utilizzano la tabella partizionata.

Utilizzo di una vista invece della tabella padre.

La soluzione per questo problema consiste nel creare una visualizzazione che interroga la tabella padre e indirizzare le istruzioni INSERT alla visualizzazione. L'inserimento in una vista può sembrare pazzesco, ma è qui che entra in gioco il grilletto della vista.

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

Interrogare questa vista sarà proprio come interrogare la tabella principale e le clausole WHERE e JOINS funzioneranno come previsto.

Visualizza funzione e trigger specifici

Invece di usare la funzione e il trigger che abbiamo definito in precedenza, entrambi saranno leggermente diversi. Modifiche in grassetto.

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();

La definizione "INVECE DI" assume il comando di inserimento sulla vista (che comunque non funzionerebbe) ed esegue invece la funzione. La funzione che abbiamo definito ha un requisito molto specifico di fare un "RETURN NEW" dopo che l'inserimento nelle tabelle figlio è stato completato. Senza questo (o facendolo come abbiamo fatto prima con "RETURN NULL") risulterà "INSERT 0 0" invece di "INSERT 0 1" come ci aspetteremmo.

Esempio:

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)

Le applicazioni che verificano la correttezza del "numero di righe" inserito troveranno questa correzione per funzionare come previsto. In questo esempio, abbiamo aggiunto _view alla nostra vista e alla procedura memorizzata, ma se si desidera che la tabella venga partizionata senza che gli utenti lo sappiano/modificano l'applicazione, rinomineremmo la tabella padre in data_log_parent e chiameremo la vista dal vecchio nome della tabella padre.

Aggiornamento di una riga e modifica del valore della colonna partizionata

Una cosa da tenere presente è che se si esegue un aggiornamento sui dati nella tabella partizionata e si modifica il valore della colonna in un valore non consentito dal vincolo risulterà in un errore. Se questo tipo di aggiornamento non avverrà mai, allora può essere ignorato, ma se è possibile, è necessario scrivere un nuovo trigger per i processi UPDATE che cancellerà effettivamente la riga dalla vecchia partizione figlio e ne inserirà una nuova nella nuova partizione figlio di destinazione.

Creazione di partizioni future

La creazione di partizioni future può essere eseguita in diversi modi, ognuno con i propri pro e contro.

Futuro creatore di partizioni

Un programma esterno può essere scritto per creare partizioni future X volte prima che siano necessarie. In un esempio di partizionamento partizionato in base a una data, la successiva partizione necessaria da creare (nel nostro caso 2019) potrebbe essere impostata per essere creata a dicembre. Questo può essere uno script manuale eseguito dall'amministratore del database o impostato in modo che cron lo esegua quando necessario. Partizioni annuali significherebbero che viene eseguito una volta all'anno, tuttavia le partizioni giornaliere sono comuni e un lavoro cron giornaliero rende un DBA più felice.

Creatore automatico di partizioni

Con la potenza di plpgsql, possiamo catturare gli errori se proviamo a inserire dati in una partizione figlio che non esiste e creare al volo la partizione necessaria, quindi provare a inserirli di nuovo. Questa opzione funziona bene tranne nel caso in cui molti client diversi inseriscono dati simili contemporaneamente, potrebbero causare una condizione di competizione in cui un client crea la tabella, mentre un altro tenta di creare la stessa tabella e ottiene un errore già esistente. Una programmazione plpgsql intelligente e avanzata può risolvere questo problema, ma è in discussione se valga o meno il livello di sforzo. Se questa condizione di gara non si verificherà a causa dei modelli di inserto, non c'è nulla di cui preoccuparsi.

Eliminazione delle partizioni

Se le regole di conservazione dei dati impongono che i dati vengano eliminati dopo un certo periodo di tempo, ciò diventa più semplice con le tabelle partizionate se partizionate su una colonna data. Se dovessimo eliminare i dati che hanno 10 anni, potrebbe essere semplice come:

severalnines=# DROP TABLE part.data_log_2007;
DROP TABLE

Questo è molto più rapido ed efficiente di un'istruzione "DELETE", poiché non risulta in tuple morte da pulire con un aspirapolvere.

Nota:se si rimuovono le tabelle dall'impostazione della partizione, anche il codice nelle funzioni di attivazione deve essere modificato per non indirizzare la data alla tabella eliminata.

Cose da sapere prima del partizionamento

Il partizionamento delle tabelle può offrire un drastico miglioramento delle prestazioni, ma potrebbe anche peggiorare le cose. Prima di eseguire il push ai server di produzione, la strategia di partizionamento dovrebbe essere testata in modo approfondito, per coerenza dei dati, velocità delle prestazioni, tutto. Il partizionamento di una tabella ha alcune parti mobili, dovrebbero essere tutte testate per assicurarsi che non ci siano problemi.

Quando si tratta di decidere il numero di partizioni, si consiglia vivamente di mantenere il numero di tabelle figlio al di sotto di 1000 tabelle e, se possibile, anche inferiore. Una volta che il conteggio della tabella figlio supera ~1000, le prestazioni iniziano a diminuire poiché il pianificatore di query stesso finisce per impiegare molto più tempo solo per creare il piano di query. Non è raro che un piano di query richieda molti secondi, mentre l'esecuzione effettiva richiede solo pochi millisecondi. Se si eseguono migliaia di query al minuto, alcuni secondi potrebbero bloccare le applicazioni.

Le stored procedure del trigger plpgsql possono anche complicarsi e, se troppo complicate, anche rallentare le prestazioni. La stored procedure viene eseguita una volta per ogni riga inserita nella tabella. Se finisce per eseguire troppa elaborazione per ogni riga, gli inserimenti potrebbero diventare troppo lenti. I test delle prestazioni assicureranno che sia ancora in un intervallo accettabile.

Diventa creativo

Il partizionamento delle tabelle in PostgreSQL può essere tanto avanzato quanto necessario. Invece delle colonne della data, le tabelle possono essere partizionate su una colonna "paese", con una tabella per ogni paese. Il partizionamento può essere eseguito su più colonne, come sia una colonna "data" che una "paese". Ciò renderà più complessa la procedura memorizzata che gestisce gli inserti, ma è possibile al 100%.

Ricorda, gli obiettivi con il partizionamento sono suddividere tabelle estremamente grandi in tabelle più piccole e farlo in un modo ben congegnato per consentire al pianificatore di query di accedere ai dati più velocemente di quanto potrebbe avere nella tabella originale più grande.

Partizionamento dichiarativo

In PostgreSQL 10 e versioni successive è stata introdotta una nuova funzionalità di partizionamento "Partizionamento dichiarativo". È un modo più semplice per configurare le partizioni, tuttavia presenta alcune limitazioni. Se le limitazioni sono accettabili, probabilmente funzionerà più velocemente rispetto all'impostazione manuale della partizione, ma un'abbondante quantità di test lo verificherà.

La documentazione ufficiale di postgresql contiene informazioni sul partizionamento dichiarativo e su come funziona. È una novità in PostgreSQL 10 e con la versione 11 di PostgreSQL all'orizzonte al momento della stesura di questo articolo, alcune limitazioni sono state corrette, ma non tutte. Con l'evoluzione di PostgreSQL, il partizionamento dichiarativo può diventare un sostituto completo del partizionamento più complesso trattato in questo articolo. Fino ad allora, il partizionamento dichiarativo potrebbe essere un'alternativa più semplice se nessuna delle limitazioni limita le esigenze di partizionamento.

Limitazioni dichiarative del partizionamento

La documentazione di PostgreSQL affronta tutte le limitazioni con questo tipo di partizionamento in PostgreSQL 10, ma è possibile trovare un'ottima panoramica su The Official PostgreSQL Wiki che elenca le limitazioni in un formato più facile da leggere, oltre a notare quali sono state corrette in il prossimo PostgreSQL 11.

Chiedi alla community

Gli amministratori di database di tutto il mondo progettano da molto tempo strategie di partizionamento avanzate e personalizzate e molti di noi frequentano IRC e mailing list. Se è necessario aiuto per decidere la strategia migliore o semplicemente per risolvere un bug in una stored procedure, la community è qui per aiutarti.

  • IRC
    Freenode ha un canale molto attivo chiamato #postgres, in cui gli utenti si aiutano a vicenda a comprendere concetti, correggere errori o trovare altre risorse.
  • Mailing List
    PostgreSQL ha una manciata di mailing list che possono essere unite. Domande/problemi di forma più lunga possono essere inviati qui e possono raggiungere molte più persone di IRC in qualsiasi momento. Gli elenchi possono essere trovati sul sito Web di PostgreSQL e gli elenchi pgsql-general o pgsql-admin sono buone risorse.