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

Sharding dei dati con PostgreSQL 11

La versione 10 di PostgreSQL ha aggiunto il partizionamento dichiarativo delle tabelle funzionalità. Nella versione 11 (attualmente in versione beta), puoi combinarlo con datawrapper stranieri , fornendo un meccanismo per partizionare in modo nativo le tabelle su più server PostgreSQL.

Partizionamento dichiarativo

Considera una tabella che memorizza le temperature minime e massime giornaliere delle città per ogni giorno:

CREATE TABLE temperatures (
    at      date,
    city    text,
    mintemp integer,
    maxtemp integer
);

Le specifiche della tabella sono intenzionalmente prive di vincoli di colonna e chiave primaria per semplificare le cose:le aggiungeremo in seguito.

È molto comune scoprire che in molte applicazioni si accede più frequentemente ai dati più recenti. Pensa all'anno finanziario in corso, a questo mese, all'ultima ora e così via. Man mano che la nostra tabella delle "temperature" cresce, ha senso spostare i vecchi dati in un'altra tabella, con la stessa struttura. Possiamo, ad esempio, fare questo:

CREATE TABLE temperatures_2017 (LIKE temperatures);
INSERT INTO temperatures_2017 SELECT * FROM temperatures WHERE
	extract(year from at) = 2017;
DELETE FROM temperatures WHERE extract(year from at) = 2017;

per spostare tutte le voci dell'anno 2017 in un'altra tabella. Ciò lascia la tabella "temperature" principale più piccola e veloce con cui l'applicazione può lavorare. Come bonus, se ora hai bisogno di eliminare i vecchi dati, puoi farlo senza rallentare gli inserimenti di dati in entrata nella tabella principale/corrente perché i vecchi dati sopravvivono in un'altra tabella.

Ma avere più tabelle distinte significa che ora il codice dell'applicazione deve cambiare. Se deve accedere a dati più vecchi, ad esempio ottenere le temperature minime e massime annuali di una città, ora deve scoprire quali tabelle sono presenti nello schema, interrogare ciascuna di esse e combinare i risultati di ciascuna tabella. Possiamo farlo senza modificare il codice dell'applicazione?

Il partizionamento lo rende possibile. In PostgreSQL 10, puoi creare la tabella "temperature" in questo modo:

CREATE TABLE temperatures (
    at      date,
    city    text,
    mintemp integer,
    maxtemp integer
)
PARTITION BY RANGE (at);

Questo rende "temperature" una tabella master di partizione e dice a PostgreSQL che creeremo più tabelle partizionate che memorizzano dati non sovrapposti, ciascuna con un diverso set di valori "at". La stessa tabella master non contiene alcun dato, ma può essere interrogata e inserita dall'applicazione, che ignora le partizioni figlio che contengono i dati effettivi.

Ed ecco le nostre partizioni:

CREATE TABLE temperatures_2017
    PARTITION OF temperatures
    FOR VALUES FROM ('2017-01-01') TO ('2018-01-01');

CREATE TABLE temperatures_2018
    PARTITION OF temperatures
    FOR VALUES FROM ('2018-01-01') TO ('2019-01-01');

Ora abbiamo due tabelle, una che memorizzerà i dati per il 2017 e un'altra per il 2018. Nota che il valore "da" è inclusivo, ma il valore "a" non lo è. Proviamolo:

temp=# INSERT INTO temperatures (at, city, mintemp, maxtemp)
temp-#        VALUES ('2018-08-03', 'London', 63, 90);
INSERT 0 1
temp=# INSERT INTO temperatures (at, city, mintemp, maxtemp)
temp-#        VALUES ('2017-08-03', 'London', 59, 70);
INSERT 0 1
temp=# SELECT * FROM temperatures;
     at     |  city  | mintemp | maxtemp
------------+--------+---------+---------
 2017-08-03 | London |      59 |      70
 2018-08-03 | London |      63 |      90
(2 rows)

temp=# SELECT * FROM temperatures_2017;
     at     |  city  | mintemp | maxtemp
------------+--------+---------+---------
 2017-08-03 | London |      59 |      70
(1 row)

temp=# SELECT * FROM temperatures_2018;
     at     |  city  | mintemp | maxtemp
------------+--------+---------+---------
 2018-08-03 | London |      63 |      90
(1 row)

L '"applicazione" è in grado di inserire e selezionare dalla tabella principale, ma PostgreSQL instrada i dati effettivi nelle tabelle figlio appropriate. (Oh e BTW, quelle temperature sono reali!)

Indici e vincoli

Gli indici e i vincoli di tabelle e colonne sono in realtà definiti a livello di partizionamento, poiché è lì che risiedono i dati effettivi. Puoi impostarli durante la creazione della tabella delle partizioni:

CREATE TABLE temperatures_2017
    PARTITION OF temperatures (
        mintemp NOT NULL,
        maxtemp NOT NULL,
        CHECK (mintemp <= maxtemp),
        PRIMARY KEY (at, city)
    )
    FOR VALUES FROM ('2017-01-01') TO ('2018-01-01');

PostgreSQL 11 ti consente di definire gli indici sulla tabella padre e creerà indici sulle tabelle delle partizioni esistenti e future. Leggi di più qui.

Wrapper di dati stranieri

La funzionalità di wrapper di dati esterni esiste da tempo in Postgres. PostgreSQL ti consente di accedere ai dati archiviati in altri server e sistemi utilizzando questo meccanismo. Quello che ci interessa è "postgres_fdw", che è ciò che ci permetterà di accedere a un server Postgres da un altro.

“postgres_fdw” è un'estensione presente nella distribuzione standard, che può essere installata con il normale comando CREATE EXTENSION:

CREATE EXTENSION postgres_fdw;

Supponiamo che tu abbia un altro server PostgreSQL "box2" con un database chiamato "box2db". Puoi creare un "server estero" per questo:

CREATE SERVER box2 FOREIGN DATA WRAPPER postgres_fdw
    OPTIONS (host 'box2', dbname 'box2db');

Mappiamo anche il nostro utente "alice" (l'utente con cui hai effettuato l'accesso) all'utente box2 "box2alice". Ciò consente ad "alice" di essere "box2alice" quando si accede a tavoli remoti:

CREATE USER MAPPING FOR alice SERVER box2
    OPTIONS (user 'box2alice');

Ora puoi accedere alle tabelle (anche viste, matview ecc.) su box2. Per prima cosa, crea una tabella su box2, quindi una "tabella straniera" sul tuo server. La tabella esterna non contiene dati effettivi, ma funge da proxy per l'accesso alla tabella box2.

-- on box2
CREATE TABLE foo (a int);

-- on your server
IMPORT FOREIGN SCHEMA public LIMIT TO (foo)
    FROM SERVER box2 INTO public;

La tabella esterna nel tuo server può partecipare alle transazioni allo stesso modo delle normali tabelle. Le applicazioni non devono sapere che le tabelle con cui interagisce sono locali o esterne, anche se se la tua app esegue un SELECT che potrebbe inserire molte righe da una tabella esterna potrebbe rallentare le cose. In Postgres 10, sono stati apportati miglioramenti per spingere verso il basso i join e aggregato al server remoto.

Combinazione di partizionamento e FDW

E ora la parte divertente:configurare le partizioni su server remoti.

Per prima cosa, creiamo la tabella delle partizioni fisiche su box2:

-- on box2
CREATE TABLE temperatures_2016 (
    at      date,
    city    text,
    mintemp integer,
    maxtemp integer
);

E quindi crea la partizione sul tuo server, come tabella esterna:

CREATE FOREIGN TABLE temperatures_2016
    PARTITION OF temperatures
    FOR VALUES FROM ('2016-01-01') TO ('2017-01-01')
    SERVER box2;

Ora puoi inserire e interrogare dal tuo server:

temp=# INSERT INTO temperatures (at, city, mintemp, maxtemp)
temp-#     VALUES ('2016-08-03', 'London', 63, 73);
INSERT 0 1
temp=# SELECT * FROM temperatures ORDER BY at;
     at     |  city  | mintemp | maxtemp
------------+--------+---------+---------
 2016-08-03 | London |      63 |      73
 2017-08-03 | London |      59 |      70
 2018-08-03 | London |      63 |      90
(3 rows)

temp=# SELECT * FROM temperatures_2016;
     at     |  city  | mintemp | maxtemp
------------+--------+---------+---------
 2016-08-03 | London |      63 |      73
(1 row)

Ecco qua! Essere in grado di inserire righe in una partizione remota è una novità nella versione 11. Con questa funzione, ora puoi dividere i tuoi dati in modo logico (partizioni) e fisicamente (FDW).

Gestione dati

Comandi come VACUUM e ANALYZE funzionano come ti aspetteresti con le tabelle master delle partizioni:tutte le tabelle figlio locali sono soggette a VACUUM e ANALYZE. Le partizioni possono essere staccate, i dati vengono manipolati senza il vincolo della partizione e quindi ricollegati. Le stesse tabelle figlio della partizione possono essere partizionate.

Lo spostamento dei dati ("resharding") può essere eseguito con normali istruzioni SQL (inserisci, elimina, copia ecc.). È possibile creare indici e trigger di partizione locale.

L'aggiunta di ridondanza agli shard è facilmente realizzabile con la replica logica o in streaming.