2ndQuadrant » giulio.calacoci https://blog.2ndquadrant.it Il blog sui database di 2ndQuadrant Italia Thu, 25 Jan 2018 11:36:59 +0000 en-US hourly 1 http://wordpress.org/?v=4.3.15 Rilasciato Barman 1.6.0 https://blog.2ndquadrant.it/rilascio-della-release-1-6-0-di-barman/ https://blog.2ndquadrant.it/rilascio-della-release-1-6-0-di-barman/#comments Mon, 29 Feb 2016 10:39:33 +0000 http://blog.2ndquadrant.it/?p=2810 2ndQuadrant è orgogliosa di annunciare il rilascio della versione 1.6.0 di Barman, “Backup And Recovery Manager” per istanze PostgreSQL. Questa nuova release introduce il supporto allo streaming dei WAL file, migliorando drasticamente la sicurezza delle soluzioni di backup per PostgreSQL basate su Barman e riducendo il Recovery Point Objective (RPO) quasi a 0. Per un corretto funzionamento, Barman necessita ancora dell’archiviazione tramite archive_command di PostgreSQL. Questo limite è destinato a essere rimosso non appena barman sarà in grado di supportare i Replication Slot. Un’approfondita attività di refactoring ed una maggiore attenzione ai controlli, rendono Barman ancora più stabile e robusto. Vi consigliamo quindi di procedete con l’aggiornamento il prima possibile. Per una lista completa delle modifiche, vi invitiamo a leggere il comunicato ufficiale in lingua inglese.

Cos’è Barman

Barman (Backup And Recovery Manager per PostgreSQL) è un software open-source scritto in Python. Permette di eseguire backup remoti su più server in ambienti business critical e di supportare gli amministratori di database durante la fase di recovery. Le funzionalità più apprezzate di Barman sono: cataloghi di backup, backup incrementale, retention policy, backup remoto e recovery, archiviazione e compressione dei file WAL e backup. Barman è progettato, implementato e mantenuto da 2ndQuadrant Italia e distribuito secondo licenza GNU GPL 3.

]]>
https://blog.2ndquadrant.it/rilascio-della-release-1-6-0-di-barman/feed/ 0
PostgreSQL 9.5: partecipazione delle tabelle esterne all’ereditarietà https://blog.2ndquadrant.it/postgresql-9-5-eredita-tabelle-esterne/ https://blog.2ndquadrant.it/postgresql-9-5-eredita-tabelle-esterne/#comments Mon, 09 Nov 2015 10:00:24 +0000 http://blog.2ndquadrant.it/?p=2491 Dalla versione 9.1, PostgreSQL supporta il concetto di ereditarietà applicato alle tabelle. Questo era possibile esclusivamente per le tabelle locali. Con la nuova feature le tabelle esterne potranno partecipare come membri all’ereditarietà. PostgreSQL 9.5 offre, inoltre, la possibilità di effettuare lo sharding senza bisogno di estensioni aggiuntive.


tab-esterne-ereditarieta

Ereditarietà in PostgreSQL

Vediamo innanzitutto il significato di ereditarietà all’interno del contesto di un database relazionale.

L’ereditarietà permette a una tabella, detta “figlia”, di acquisire tutte le caratteristiche di una tabella già esistente, detta “padre”. In questo modo la tabella “figlia” possiede la stessa struttura e gli stessi vincoli della tabella “padre” in aggiunta alle colonne proprie della sua struttura. Effettuando una query sulla tabella “padre” otteniamo come risultato i record combinati della tabella “padre” e “figlia”, interrogando la tabella “figlia” non è, però, possibile ottenere i record della tabella da cui eredita. Questo fornisce alcune interessanti opzioni quando si tratta di progettare un database.

Piccolo esempio di ereditarietà

Supponiamo di avere una tabella che contenga dati anagrafici di attori, chiamata “actors”, e una tabella che contenga i dati anagrafici degli attori vincitori di un premio Oscar, chiamata “oscar_actors”. È facile dedurre che tutti gli attori presenti nella seconda tabella appartengono legittimamente anche alla prima, ma non il contrario.

Le due tabelle sono state create con i seguenti statement:

CREATE TABLE actors (
    id serial primary key,
    first_name text not null,
    last_name text not null
);

CREATE TABLE oscar_actors (
    id serial primary key,
    first_name text not null,
    last_name text not null,
    oscar_prize text not null
    );

Nota Bene: Tutti gli esempi presenti in questo articolo usano come utente di riferimento “postgres”. Questa è una scelta effettuata per motivi di semplicità e per non deviare dall’argomento trattato ed è una pessima scelta in fatto di sicurezza del vostro database. Vi suggerisco di utilizzare un utente specifico per la vostra applicazione.

Per semplificare la consultazione delle due tabelle possiamo creare una vista che le metta in correlazione tramite una query che usa l’operatore ‘UNION’. Una soluzione alternativa prevede che le tabelle siano strutturate in maniera diversa, usando l’ereditarietà.

CREATE TABLE actors (
    id serial primary key,
    first_name text not null,
    last_name text not null
);

CREATE TABLE oscar_actors (
    oscar_prize text not nul
    ) INHERITS (actors);

Come spiegato, la tabella “oscar_actors” eredita tutte le colonne della tabella “actors”. La tabella “oscar_actors” possiede inoltre la colonna aggiuntiva “oscar_prize” che mostra quale è il premio oscar vinto dall’attore.

Eseguendo una query sulla tabella “actors”, per esempio cercando tutti gli attori di nome Michael presenti nel nostro set di dati, il risultato è la combinazione dei contenuti delle due tabelle, “padre” e “figlia”.

Interrogando la tabella “oscar_actors”, invece, otterremmo i nomi di tutti i Michael che hanno vinto un premio Oscar.

Ereditarietà e tabelle esterne

È possibile usare l’ereditarietà per “partizionare” una tabella in maniera “orizzontale”, ovvero per suddividere una tabella in sottotabelle più piccole. Ogni sottotabella contiene un insieme di record suddivisi secondo un criterio comune (ad esempio il mese di inserimento nel caso di una tabella storicizzata).

L’inserimento delle tabelle esterne in questo scenario rende tutto ancora più interessante!

Le varie sottotabelle possono risiedere tutte su database separati, rendendo possibile l’implementazione di una tecnica di partizionamento nota come sharding.

Implementazione dello sharding tramite tabelle esterne

Vediamo ora come è possibile attuare lo sharding tramite tabelle esterne. Supponiamo di avere un database di nome “master” 4 database di nome “db1”, “db2”, “db3” e “db4”. Su tutti i database creiamo una tabella con questa struttura:

CREATE TABLE products (
    id serial PRIMARY KEY,
    name TEXT NOT NULL);

Connettiamoci quindi al database “master”, creiamo una tabella “products” e produciamo quindi 4 tabelle esterne che ereditano da questa:

CREATE TABLE products (id serial PRIMARY KEY, name TEXT NOT NUll);
CREATE EXTENSION postgres_fdw;
CREATE SERVER db1 FOREIGN DATA WRAPPER postgres_fdw OPTIONS( dbname 'db1' );
CREATE SERVER db2 FOREIGN DATA WRAPPER postgres_fdw OPTIONS( dbname 'db2' );
CREATE SERVER db3 FOREIGN DATA WRAPPER postgres_fdw OPTIONS( dbname 'db3' );
CREATE SERVER db4 FOREIGN DATA WRAPPER postgres_fdw OPTIONS( dbname 'db4' );
CREATE USER MAPPING FOR POSTGRES SERVER db1 OPTIONS ( user 'postgres' );
CREATE USER MAPPING FOR POSTGRES SERVER db2 OPTIONS ( user 'postgres' );
CREATE USER MAPPING FOR POSTGRES SERVER db3 OPTIONS ( user 'postgres' );
CREATE USER MAPPING FOR POSTGRES SERVER db4 OPTIONS ( user 'postgres' );
CREATE FOREIGN TABLE products_db1 () INHERITS (products) SERVER db1 OPTIONS ( table_name 'products' );
CREATE FOREIGN TABLE products_db2 () INHERITS (products) SERVER db2 OPTIONS ( table_name 'products' );
CREATE FOREIGN TABLE products_db3 () INHERITS (products) SERVER db3 OPTIONS ( table_name 'products' );
CREATE FOREIGN TABLE products_db4 () INHERITS (products) SERVER db4 OPTIONS ( table_name 'products' );

Creiamo quindi 4 sequenze su “master” per l’assegnazione degli id alle tabelle esterne.

CREATE SEQUENCE products_id_seq_db1 INCREMENT BY 4 RESTART WITH 1;
CREATE SEQUENCE products_id_seq_db2 INCREMENT BY 4 RESTART WITH 2;
CREATE SEQUENCE products_id_seq_db3 INCREMENT BY 4 RESTART WITH 3;
CREATE SEQUENCE products_id_seq_db4 INCREMENT BY 4 RESTART WITH 4;
ALTER FOREIGN TABLE products_db1 ALTER COLUMN id SET DEFAULT nextval('products_id_seq_db1');
ALTER FOREIGN TABLE products_db2 ALTER COLUMN id SET DEFAULT nextval('products_id_seq_db2');
ALTER FOREIGN TABLE products_db3 ALTER COLUMN id SET DEFAULT nextval('products_id_seq_db3');
ALTER FOREIGN TABLE products_db4 ALTER COLUMN id SET DEFAULT nextval('products_id_seq_db4');

Le sequenze degli id delle varie tabelle sono state alterate in modo che abbiano un incremento di 4 e uno sfasamento nel numero di partenza. Gli id generati per le 4 tabelle sono quindi diversi.

Inseriamo, quindi, 4 prodotti direttamente nelle tabelle esterne:

INSERT INTO products_db1 (name) VALUES ('playstation');
INSERT INTO products_db2 (name) VALUES ('xbox');
INSERT INTO products_db3 (name) VALUES ('wii');
INSERT INTO products_db4 (name) VALUES ('saturn');

Facendo una SELECT sulla tabella “products” troviamo tutti i prodotti che abbiamo appena inserito:

master=# SELECT * FROM products;
 id |    name
----+-------------
  1 | playstation
  2 | xbox
  3 | wii
  4 | saturn
(4 rows)

Eseguendo un EXPLAIN ANALIZE possiamo vedere l’esecuzione delle scansioni sulle tabelle esterne:

master=# EXPLAIN ANALYZE SELECT * FROM products;
                                                       QUERY PLAN
------------------------------------------------------------------------------------------------------------------------
 Append  (cost=0.00..603.80 rows=5461 width=36) (actual time=0.693..2.682 rows=4 loops=1)
   ->  Seq Scan on products  (cost=0.00..0.00 rows=1 width=36) (actual time=0.001..0.001 rows=0 loops=1)
   ->  Foreign Scan on products_db1  (cost=100.00..150.95 rows=1365 width=36) (actual time=0.689..0.690 rows=1 loops=1)
   ->  Foreign Scan on products_db2  (cost=100.00..150.95 rows=1365 width=36) (actual time=0.668..0.669 rows=1 loops=1)
   ->  Foreign Scan on products_db3  (cost=100.00..150.95 rows=1365 width=36) (actual time=0.665..0.666 rows=1 loops=1)
   ->  Foreign Scan on products_db4  (cost=100.00..150.95 rows=1365 width=36) (actual time=0.650..0.652 rows=1 loops=1)
 Planning time: 0.316 ms
 Execution time: 4.601 ms
(8 rows)

è possibile modificare anche i record delle tabelle figlie, per esempio con facendo una UPDATE:

master=# EXPLAIN ANALYZE UPDATE products SET name = 'wii-u' WHERE ID = 4 AND name LIKE 'wii';
                                                     QUERY PLAN
---------------------------------------------------------------------------------------------------------------------
 Update on products  (cost=0.00..587.52 rows=5 width=10) (actual time=3.387..3.387 rows=0 loops=1)
   Update on products
   Foreign Update on products_db1
   Foreign Update on products_db2
   Foreign Update on products_db3
   Foreign Update on products_db4
   ->  Seq Scan on products  (cost=0.00..2.91 rows=1 width=10) (actual time=0.018..0.018 rows=0 loops=1)
         Filter: ((name ~~ 'wii'::text) AND (id = 4))
         Rows Removed by Filter: 1
   ->  Foreign Scan on products_db1  (cost=100.00..146.15 rows=1 width=10) (actual time=0.878..0.878 rows=0 loops=1)
   ->  Foreign Scan on products_db2  (cost=100.00..146.15 rows=1 width=10) (actual time=0.861..0.861 rows=0 loops=1)
   ->  Foreign Scan on products_db3  (cost=100.00..146.15 rows=1 width=10) (actual time=0.759..0.759 rows=0 loops=1)
   ->  Foreign Scan on products_db4  (cost=100.00..146.15 rows=1 width=10) (actual time=0.866..0.866 rows=0 loops=1)
 Planning time: 0.786 ms
 Execution time: 5.718 ms
(15 rows)

Lo sharding non è ancora completo. Ogni volta che facciamo una query viene eseguita una scansione di tutte le tabelle, anche quando non sarebbe necessario.

master=# EXPLAIN ANALYZE SELECT * FROM products WHERE name LIKE 'xbox';
                                                     QUERY PLAN
---------------------------------------------------------------------------------------------------------------------
 Append  (cost=0.00..508.81 rows=29 width=36) (actual time=1.478..2.845 rows=1 loops=1)
   ->  Seq Scan on products  (cost=0.00..0.00 rows=1 width=36) (actual time=0.002..0.002 rows=0 loops=1)
         Filter: (name ~~ 'xbox'::text)
   ->  Foreign Scan on products_db1  (cost=100.00..127.20 rows=7 width=36) (actual time=0.740..0.740 rows=0 loops=1)
   ->  Foreign Scan on products_db2  (cost=100.00..127.20 rows=7 width=36) (actual time=0.734..0.736 rows=1 loops=1)
   ->  Foreign Scan on products_db3  (cost=100.00..127.20 rows=7 width=36) (actual time=0.631..0.631 rows=0 loops=1)
   ->  Foreign Scan on products_db4  (cost=100.00..127.20 rows=7 width=36) (actual time=0.733..0.733 rows=0 loops=1)
 Planning time: 0.459 ms
 Execution time: 4.878 ms
(9 rows)

Il problema è risolvibile affidandosi ai meccanismi di partizionamento già presenti in PostgreSQL. Aggiungiamo quindi alle tabelle esterne un constraint di tipo CHECK, definendo così quali siano le chiavi che appartengono a quella partizione

ALTER FOREIGN TABLE products_db1 ADD CHECK ( id % 4 = 1 );
ALTER FOREIGN TABLE products_db2 ADD CHECK ( id % 4 = 2 );
ALTER FOREIGN TABLE products_db3 ADD CHECK ( id % 4 = 3 );
ALTER FOREIGN TABLE products_db4 ADD CHECK ( id % 4 = 0 );

In questo caso abbiamo scelto come discriminante un criterio numerico basato sull’operazione di modulo 4 (perché 4 sono le tabelle) del campo id.

master=# EXPLAIN ANALYZE SELECT * FROM products WHERE id = 4 AND (id % 4) = (4 % 4) ;
                                                     QUERY PLAN
---------------------------------------------------------------------------------------------------------------------
 Append  (cost=0.00..137.13 rows=2 width=36) (actual time=0.913..0.915 rows=1 loops=1)
   ->  Seq Scan on products  (cost=0.00..3.22 rows=1 width=36) (actual time=0.013..0.013 rows=0 loops=1)
         Filter: ((id = 4) AND ((id % 4) = 0))
         Rows Removed by Filter: 1
   ->  Foreign Scan on products_db4  (cost=100.00..133.91 rows=1 width=36) (actual time=0.900..0.901 rows=1 loops=1)
 Planning time: 0.525 ms
 Execution time: 1.591 ms
(7 rows)

PostgreSQL in questo modo identifica immediatamente a quale tabella appartiene l’id che stiamo cercando e evita di fare la scansione delle altre.

Gli inserimenti sono ancora non “automatici” ma basta creare un trigger BEFORE INSERT per la tabella users che applichi gli stessi criteri applicati per i vincoli usati per le tabelle esterne alle linee in inserimento.

Conclusioni

La partecipazione delle tabelle esterne all’ereditarietà è una feature di grande importanza. Finora per poter partizionare orizzontalmente le tabelle era necessario ricorrere a strumenti esterni a PostgreSQL come Pg_shard o PL/Proxy. Dalla 9.5 tutto quello che serve per costruire l’architettura per lo sharding è presente all’interno del core di PostgreSQL.

Sono molteplici i benefici che è possibile trarre da una attenta pianificazione del partizionamento delle tabelle. È possibile migliorare le prestazioni delle query su tabelle di grandi dimensioni, riducendone il set di dati da analizzare. Inoltre, ripartendo il carico delle dimensioni di alcune tabelle molto grandi su diverse istanze di PostgreSQL, si riduce lo spazio occupato sul disco, non solo dal database ma anche dai suoi backup, rendendo più rapide se necessario le operazioni di ripristino dei cluster.

]]>
https://blog.2ndquadrant.it/postgresql-9-5-eredita-tabelle-esterne/feed/ 0
PostgreSQL 9.5: IMPORT FOREIGN SCHEMA https://blog.2ndquadrant.it/postgresql-9-5-import-foreign-schema/ https://blog.2ndquadrant.it/postgresql-9-5-import-foreign-schema/#comments Wed, 08 Apr 2015 08:30:22 +0000 http://blog.2ndquadrant.it/?p=2104 Il rilascio di PostgreSQL 9.5 è vicino ed è arrivato il momento di analizzare le novità di questa nuova release.
Una funzionalità molto interessante della versione 9.5 sarà la possibilità di importare uno schema da un database remoto, tramite l’utilizzo di Foreign Data Wrapper e del comando IMPORT FOREIGN SCHEMA.

import-foreign-schema

I Foreign Data Wrapper (FDW)

Prima dell’introduzione dei Foreign Data Wrapper l’unico modo per connettere un database Postgres con una fonte dati esterna era il modulo dblink.
Nel 2003 viene definito all’interno del linguaggio SQL l’insieme di regole per la gestione in maniera standard di fonti di dati esterne: SQL/MED (management of external Data).

All’interno di PostgreSQL 9.1, una prima implementazione dello standard SQL/MED viene introdotta con i Foreign Data Wrapper, fornendo a Postgres l’accesso diretto a fonti di dati come file o altri database (Oracle, Mysql…), permettendone l’utilizzo come tabelle.
Il vantaggio di questo approccio è evidente: la possibilità di interrogare una fonte di dati esterna per poterne estrarre dati in maniera nativa eseguendo una semplice query. Il non dover ricorrere a moduli esterni per ottenere questo risultato è una notevole semplificazione del lavoro per i DBA.

Per saperne di più, potete dare una occhiata all’articolo pubblicato nel 2011, prima dell’uscita di PostgreSQL 9.1,  sul nostro blog: “PostgreSQL 9.1: Tabelle esterne con SQL/MED“.

Piccolo esempio di uso di un FDW

PostgreSQL 9.3 introduce il supporto ai foreign data wrapper in scrittura ed aggiunge anche il supporto al foreign data wrapper per PostgreSQL. Vediamo adesso un semplice esempio di utilizzo di un FDW connettendo fra loro due database Postgres.
Creiamo due database:

CREATE DATABASE source;
CREATE DATABASE destination;

All’interno di source creiamo una tabella di test con dei dati di test:

\c source
CREATE TABLE test1 AS SELECT id, md5(random()::text) FROM generate_series(1,5) id;

Connettiamoci adesso al db di destinazione e connettiamo i due database:

\c destination
CREATE EXTENSION postgres_fdw ;
CREATE SERVER src_srv FOREIGN DATA WRAPPER postgres_fdw OPTIONS( dbname 'source' );
CREATE USER MAPPING FOR postgres SERVER src_srv OPTIONS ( user 'postgres' );

Qualcuno di voi, giustamente, si sarà alzato in piedi, lamentandosi per la pessima scelta in fatto di sicurezza! Molto bene!

Per semplicità, ho infatti deciso di connetterci con l’utente amministratore “postgres” – anche per non deviare troppo dall’argomento principale dell’articolo. Sappiate che in un ambiente di produzione, per motivi di sicurezza, dovrete prendere altre scelte – ad esempio utilizzando un utente specifico per la vostra applicazione.

Ad ogni modo, una volta stabilita la connessione, possiamo creare sul database di destinazione una tabella esterna che punti a test1 sul database source:

CREATE FOREIGN TABLE test1_ft (id integer, md5 text) server src_srv options(table_name 'test1');

Possiamo adesso confrontare il contenuto fra le due tabelle di test:

select * from test1_ft ;
 id |               md5
----+----------------------------------
  1 | 63e5bc545b45f5c3961522f2609bedd9
  2 | d74af95e495d946d4a0887c51eb2cbe2
  3 | acce7cba66967332d01d51b74eb293f7
  4 | c5bb57ca54036004de334cf793792d4e
  5 | 02f32751b09042cf28b78cc29321a32e
(5 rows)

\c source

select * from test1 ;
 id |               md5
----+----------------------------------
  1 | 63e5bc545b45f5c3961522f2609bedd9
  2 | d74af95e495d946d4a0887c51eb2cbe2
  3 | acce7cba66967332d01d51b74eb293f7
  4 | c5bb57ca54036004de334cf793792d4e
  5 | 02f32751b09042cf28b78cc29321a32e
(5 rows)

È evidente, osservando questo esempio, che uno dei più grandi limiti all’utilizzo dei Foreign Data Wrapper è la necessità di definire separatamente, conoscendone la struttura, ogni tabella.
L’accesso a dati esterni quindi risulta laborioso qualora si voglia importare tabelle più complesse o, addirittura, interi schemi.

Fino ad adesso, operazioni del genere venivano fatte per mezzo di script in grado di connettersi al database sorgente e generare la struttura delle tabelle esterne in maniera automatica.
Fortunatamente la funzionalità IMPORT FOREIGN SCHEMA, presente nella prossima release di PostgreSQL, ci viene in aiuto.

IMPORT FOREIGN SCHEMA: sinossi

L’istruzione IMPORT FOREIGN SCHEMA, permette di importare uno schema intero da una fonte dati esterna, senza dover specificare la struttura di ogni singola tabella:

IMPORT FOREIGN SCHEMA remote_schema_name
FROM SERVER server_name INTO destination_schema;

Qualora non sia necessario importare uno schema intero, è possibile usare la clausola LIMIT TO e circoscrivere l’importazione unicamente alle tabelle a cui siamo interessati:

IMPORT FOREIGN SCHEMA remote_schema_name LIMIT TO (table_name, table_name, ...)
FROM SERVER server_name INTO destination_schema;

Altrimenti, se siamo interessati a escludere solo alcune tabelle dallo schema, è possibile filtrarle con la clausola EXCLUDE:

IMPORT FOREIGN SCHEMA remote_schema_name EXCLUDE (table_name, table_name, ...)
FROM SERVER server_name INTO destination_schema;

Esempio

Vediamo nel dettaglio come utilizzare questo comando, andando a estendere l’esempio usato in precedenza.
Connettiamoci al database sorgente e aggiungiamo due tabelle a quella che già è presente:

\c source
create table test2 as select id, md5(random()::text) from generate_series(1,20) as id;
create table test3 as select id, md5(random()::text) from generate_series(1,50) as id;

Creiamo adesso nel database di destinazione uno schema che useremo come target dell’istruzione IMPORT FOREIGN SCHEMA:

\c destination
create schema imported;

Adesso possiamo importare lo schema che abbiamo appena ampliato, contando sulla connessione aperta nell’esempio precedente:

IMPORT FOREIGN SCHEMA public FROM SERVER src_srv INTO imported;

Facciamo una rapida ispezione di tutte le tabelle sul database di destinazione per osservare il risultato dell’importazione dello schema:

\dE *.*

               List of relations
  Schema  |   Name   |     Type      |  Owner
----------+----------+---------------+----------
 imported | test1    | foreign table | postgres
 imported | test2    | foreign table | postgres
 imported | test3    | foreign table | postgres
 public   | test1_ft | foreign table | postgres

All’interno dello schema public notiamo la tabella che abbiamo creato in precedenza, mentre il risultato dell’importazione “in massa” è visibile nello schema imported.

Con questo esempio è possibile constatare quanto è più veloce e immediato l’utilizzo delle tabelle esterne con IMPORT FOREIGN SCHEMA.

Conclusioni

Con PostgreSQL 9.5, grazie a questa nuova funzionalità, le migrazioni dei dati diventeranno sempre più semplici e veloci.
Attualmente l’istruzione IMPORT FOREIGN SCHEMA è supportata solo da postgres_fdw e richiede che gli sviluppatori dei singoli driver la implementino nel modo più consono alla fonte di dati.
Aumentando il numero di driver in grado di supportare questa funzionalità si aprono scenari sempre più interessanti per PostgreSQL e per l’integrazione dei dati.

]]>
https://blog.2ndquadrant.it/postgresql-9-5-import-foreign-schema/feed/ 0
Le novità di Barman 1.4.0 https://blog.2ndquadrant.it/le-novita-di-barman-1-4-0/ https://blog.2ndquadrant.it/le-novita-di-barman-1-4-0/#comments Wed, 28 Jan 2015 14:30:40 +0000 http://blog.2ndquadrant.it/?p=1888 La versione 1.4.0 di Barman aggiunge nuove funzionalità come il backup incrementale e l’integrazione automatica con pg_stat_archiver, mirate a semplificare la vita di DBA e amministratori di sistema.

Barman 1.4.0: le modifiche più importanti

L’ultima release aggiunge una nuova modalità di backup: il backup incrementale, che permette il riuso dei file non modificati fra un backup periodico e l’altro, riducendo drasticamente i tempi di esecuzione, la banda utilizzata e lo spazio occupato su disco.tazzine
Altra nuova funzionalità introdotta con la 1.4.0 è l’integrazione di Barman con la vista pg_stat_archiver, disponibile dalla versione 9.4 di PostgreSQL. Questa vista permette di raccogliere informazioni sulle performance del processo di archiviazione dei WAL e di monitorarne lo stato.
La gestione dei WAL file è migliorata. Il calcolo delle statistiche di archiviazione è stato snellito e ottimizzato. È stata potenziata la logica relativa alla rimozione dei WAL obsoleti, andando a effettuare azioni diverse nel caso di backup esclusivi o concorrenti.
I messaggi prodotti in caso di errore sono stati migliorati, rendendoli, dove possibile, più chiari e leggibili.
Abbiamo inoltre investito nella robustezza del codice: con Barman 1.4.0 disponiamo di circa 200 test di unità che vengono eseguiti ad ogni patch.

Backup Incrementale

Approfondiamo adesso la principale novità di questa release: il backup incrementale.

Definizione e teoria alla base

Per comprendere la logica su cui si basa il backup incrementale, prendiamo in considerazione due backup completi e consecutivi. Nell’intervallo di tempo che intercorre fra il completamento del primo backup e il completamento del backup successivo, non tutti i file contenuti all’interno della directory PGDATA vengono modificati. Alcuni file del backup più vecchio e del più recente, sono identici e perciò risultano ridondanti, richiedendo tempo e banda per essere trasferiti via rete e occupando spazio sul disco una volta terminata la copia. Se compariamo i file che compongono il backup più vecchio con i file che stiamo per andare a copiare dal server remoto, è possibile distinguere l’insieme di file che ha subito modifiche da quello che invece è rimasto invariato.
Con il backup incrementale diventa quindi possibile eliminarne la ridondanza, andando a copiare solamente i file modificati.

Implementazione e vantaggi tangibili

Abbiamo sviluppato questa funzionalità ponendoci tre obiettivi:

  • riduzione del tempo di esecuzione di un backup;
  • riduzione dell’utilizzo di banda;
  • riduzione dello spazio occupato eliminando le ridondanze (deduplicazione).

Per ottenerli abbiamo sfruttato la capacità di Rsync di comparare una lista di file ricevuta da un server remoto, con il contenuto di una directory locale, identificando quali siano stati modificati o meno. Abbiamo aggiunto quindi una nuova opzione di configurazione per server/globale chiamata reuse_backup. Questa opzione identifica il tipo di backup che verrà effettuato.
Vediamo i tre possibili valori di reuse_backup e i loro effetti:

  • off: valore di default, backup classico;
  • copy: identifica sul server remoto l’insieme di file modificati, utilizzando l’ultimo backup eseguito come base. Solamente i file che risultano modificati vengono trasferiti via rete, diminuendo i tempi di esecuzione di un backup e risparmiando banda. Al termine del trasferimento dal backup usato come base, vengono copiati i file non modificati, ottenendo così un backup completo;
  • link: identifica i file modificati e ne esegue la copia, esattamente come l’opzione copy. Al termine del trasferimento, il riuso dei file identificati come non modificati non viene ottenuto copiandoli, ma utilizzando degli hard link. In questo modo viene ottimizzato lo spazio sul disco occupato dal backup ed effettivamente si eliminano le ridondanze (deduplicazione).

È inoltre possibile utilizzare la seguente opzione --reuse-backup [{copy, link, off}] da linea di comando per modificare il comportamento di default per un singolo backup.
Per esempio:

$> barman backup --reuse-backup link main

forzerà il riuso del backup utilizzando gli hard link indipendentemente dal valore impostato all’interno del file di configurazione.
Utilizzerò adesso come “caso di studio” Navionics, uno dei nostri clienti e sponsor di questa release che, come vedremo, trae grossi vantaggi dall’utilizzo del backup incrementale. Navionics possiede database di notevoli dimensioni (uno dei più grandi arriva a circa 13 Terabyte). Prima dell’introduzione del backup incrementale, tenendo conto delle caratteristiche del server e della rete:

  • sarebbero state necessarie circa 52 ore per completare un backup;
  • sarebbero stati effettivamente copiati 13 TiB di dati tramite la rete;
  • sarebbero stati occupati effettivamente 13 TiB sul disco.

Con Barman 1.4.0, utilizzando reuse_backup=link e facendo barman show-backup di un backup appena terminato, Navionics ottiene:

Base backup information:
  Disk usage           : 13.2 TiB (13.2 TiB with WALs)
  Incremental size     : 5.0 TiB (-62.01%)

Il tempo di esecuzione del backup è sceso drasticamente da 52 ore a 17 ore circa. I vantaggi sono quindi evidenti:

  • il tempo di esecuzione diminuisce del 68% circa;
  • sono stati copiati via rete solo 5.0 TiB di dati al posto di 13 TiB (-62%);
  • lo spazio occupato sul disco è 5.0 TiB al posto di 13 TiB (-62%).

pg_stat_archiver: integrazione in Barman 1.4.0

Fra le novità introdotte da PostgreSQL 9.4 abbiamo la vista pg_stat_archiver che fornisce dati utili riguardanti lo stato di funzionamento del processo di archiviazione dei WAL. Grazie a queste statistiche è possibile inoltre fare previsioni sullo spazio che un nuovo backup andrà a occupare. Gli utenti di Barman 1.4.0 e PostgreSQL 9.4 potranno notare la comparsa di alcuni nuovi campi all’interno dell’output dei seguenti comandi:

  • barman check:
    • il campo booleano is_archiving che indica lo stato del processo di archiviazione.
  • barman status:
    • last_archived_time riporta l’ora di archiviazione dell’ultimo WAL file;
    • failed_count il numero di tentativi di archiviazione di WAL falliti;
    • server_archived_wals_per_hour il tasso di archiviazione di WAL/ora;
  • barman show-server aggiunge all’insieme delle statistiche del server tutti i campi che compongono la vista pg_stat_archiver.

Conclusioni

Il backup incrementale, funzionalità principale di questa release, è sicuramente uno strumento di grande utilità per tutti, permettendo di salvare tempo e spazio anche su database di dimensioni modeste. Diventa invece quasi indispensabile per tutti coloro che devono amministrare database di grosse dimensioni (VLDB) o che contengono un grosso numero di tabelle in sola lettura, fornendo un notevole incremento di prestazioni in termini di spazio occupato, tempo e banda.
Aggiungendo l’integrazione con pg_stat_archiver su PostgreSQL 9.4 migliora la capacità di monitorare lo stato dei server e quindi la salute e la robustezza di tutte quelle infrastrutture che scelgono Barman come soluzione di disaster recovery di database PostgreSQL.

]]>
https://blog.2ndquadrant.it/le-novita-di-barman-1-4-0/feed/ 0
Importare dati in Greenplum da più file csv con Talend https://blog.2ndquadrant.it/etl_con_talend_su_greenplum/ https://blog.2ndquadrant.it/etl_con_talend_su_greenplum/#comments Wed, 14 Sep 2011 15:05:01 +0000 http://2ndblog.dev.xcon.it/etl_con_talend_su_greenplum/ Lavorando con i database, capita spesso di dover importare i dati direttamente da uno o più file CSV. Esistono molteplici strumenti per farlo e spaziano dal copy di psql, via linea di comando, ai più complessi sistemi di ETL come Talend o Kettle. In questo articolo analizzeremo la capacità di Talend di interfacciarsi con un database Greenplum per importare i dati contenuti in 2 file di testo.

Dopo aver scaricato Talend Open Studio (reperibile dal sito di Talend) e dopo averlo installato, possiamo procedere creando un nuovo progetto, e all’interno di questo un nuovo job.

Creiamo quindi le connessioni alle risorse che ci serviranno durante l’importazione, ovvero la connessione al database remoto e i due file csv.

Nella sezione Metadata dell’interfaccia di Talend, aggiungiamo la connessione a Greenplum, dopo aver inserito il nome e i parametri di connessione, il programma creerà un nuovo oggetto di tipo DbConnection.

Cliccando su questo con il tasto destro e selezionando l’opzione “recupera schema”, sarà possibile selezionare le tabelle su cui vogliamo lavorare, nel nostro caso la tabella states e la tabella users.

Aggiungiamo ora i file contenenti i dati. Subito sotto l’icona delle connessioni ai database (DB connections), troviamo il gruppo dei file CSV, come precedentemente fatto per i database, clicchiamo con il tasto destro sul gruppo e poi su “crea file delimitato”.

Nella prima schermata del wizard che si aprirà, ci verrà richiesto di inserire un nome e una descrizione per la risorsa che stiamo creando. Si passa quindi allo step due, nel quale dobbiamo identificare il file di testo a cui vogliamo riferirci, e la sua codifica.

Una volta fatto sarà possibile possibile passare al terzo step, nel quale dobbiamo istruire Talend su come gestire le colonne del CSV. In questa schermata è possibile : selezionare l’encoding del file (1), i separatori di campo e i caratteri di fine linea (2), e qualora fosse necessario, segnalare quante e quali linee devono essere ignorate sia all’inizio che alla fine del file (3).

È inoltre possibile segnalare a Talend di utilizzare la prima riga come “schema” del CSV, in maniera che possa prendere i nomi delle colonne direttamente dal file, risparmiando all’utilizzatore l’inserimento manuale del nome delle colonne ( 4 ).

Nel quarto step resta solo da: modificare lo schema del file che stiamo associando, controllare e modificare i tipi di dato riconosciuti in automatico per ogni colonna e, qualora non fossero stati riconosciuti in automatico dalla testata del CSV, modificare i nomi delle colonne e la lunghezza dei campi.

Completato il 4° passo del wizard, il file è correttamente associato all’applicazione e pronto per essere usato.

Una volta aggiunte tutte le risorse che intendiamo utilizzare, è possibile procedere con la creazione del job. Trasciniamo quindi i due file sull’editor visuale del job, e selezioniamo tFileInputDelimited come tipo di file.

Dalla connessione a Greenplum precedentemente creata invece, trasciniamo sull’editor le due tabelle di destinazione (nel nostro caso states e users), e selezioniamo come tipo tGreenplumOutput.

Inseriamo ora un oggetto di tipo tMap – che si trova nella palette degli strumenti alla destra dell’editor visuale – nella cartella “elaborazione” e posizioniamolo fra il file CSV contenente gli stati da importare e la tabella di Greenplum di destinazione.

Colleghiamo il file al componente tMap (clic con il destro sul file -> riga -> main) e il componente tMap alla tabella di destinazione (clic destro sulla tabella -> riga -> nuovo output). Una volta collegati fra di loro i componenti possiamo fare doppio click sull’oggetto tMap che permette di associare le righe del file da importare a quelle della tabella di destinazione.

Trascinando i vari campi dalla tabella di sinistra a quella di destra , è possibile associare ogni colonna del file alla colonna relativa sulla tabella di destinazione. Cliccando su ok salveremo l’associazione appena effetuata e i dati sono pronti per essere importati dal file di origine alla tabella di destinazione.

Aggiungiamo ora l’importazione di una lista di utenti dal secondo csv, durante l’importazione è necessario fare lookup sulla tabella degli stati appena riempita, in maniera da verificare l’associazione “utente -> stato”, riga per riga. Con il gergo lookup si intende la ricerca di un valore all’interno di un dizionario, utilizzando una chiave al fine di recuperare un ID (solitamente la chiave primaria di quell’oggetto nel database, per garantire integrità referenziale).

Aggiungiamo quindi un oggetto tMap fra il file CSV con gli utenti e la tabella di destinazione, inoltre dalla lista delle tabelle di Greenplum trasciniamo nuovamente la tabella contenente gli stati sulla finestra dell’editor visuale, selezionando come tipo tGreenplumInput.

Colleghiamo quindi i tre elementi al componente tMap, come e’ stato fatto precedentemente.

Adesso eseguiamo il mapping fra i tre elementi: questa volta il campo idState del CSV deve essere mappato sul campo idState della tabella states (row3) e quest’ultimo deve essere collegato alla tabella utenti di destinazione. In questa maniera per ogni linea del CSV viene eseguito il lookup sulla tabella stati, assicurando così l’integrità della relazione “Utente – Stato”.

Per concludere, facciamo in modo che prima venga eseguita l’importazione dei dati relativi agli stati. Successivamente, quelli degli utenti, in modo da poter eseguire correttamente il lookup. Tracciamo quindi una riga di tipo OnComponentOK (click destro -> attivare -> onComponentOk) dal componente di output della tabella stati a quello di input degli utenti (il file CSV).

In questo modo prima verrano importati gli stati e solo in caso di successo verranno impotati gli utenti.

Adesso basterà cercare il tab di esecuzione nella parte bassa dell’area di lavoro e premere il tasto run per fare in modo che il nostro job venga eseguito.

]]>
https://blog.2ndquadrant.it/etl_con_talend_su_greenplum/feed/ 0
Come utilizzare la Virtual Machine di Greenplum Community Edition con VirtualBox https://blog.2ndquadrant.it/greenplum_vmware_virtualbox/ https://blog.2ndquadrant.it/greenplum_vmware_virtualbox/#comments Fri, 26 Aug 2011 16:30:03 +0000 http://2ndblog.dev.xcon.it/greenplum_vmware_virtualbox/ Fra i vari download disponibili nella sezione community del sito di Greenplum, è possibile trovare una Virtual Machine con il software già configurato e installato su un sistema CentOs.

La virtual machine è fatta per funzionare con VmWare, ma con qualche piccola attenzione e per puri scopi di valutazione di Greenplum, è possibile farla funzionare anche con VirtualBox.

Dopo aver installato l’ultima versione di VirtualBox (disponibile su http://www.virtualbox.org/wiki/Downloads ) è possibile scaricare l’immagine di VmWare da http://www.greenplum.com/community/downloads/. Una volta ultimato il download, e dopo aver scompattato l’archivio, è possibile procedere con l’installazione.

Innanzitutto creiamo una nuova virtual machine, semplicemente cliccando sul tasto “Nuovo” in alto a sinistra nella finestra di VirtualBox; avremo così accesso al wizard di creazione virtual machine. Dopo la prima schermata introduttiva, nella seconda ci troveremo a scegliere il nome della macchina, che deve essere univoco, e il tipo di sistema operativo. Nel primo dropdown deve essere selezionato “Linux”, mentre come versione va scelta Red Hat a 64 bit, essendo la VM basata su CentOs.

Nel terzo step del wizard viene chiesta quanta RAM deve essere allocata alla virtual machine. Di default il programma ne assegna 512 MB, ma per il tipo di ambiente che sta per essere eseguito potrebbero essere pochi. Consiglio quindi di aumentare a 1024 MB la RAM da allocare.

Nella quarta schermata deve essere deselezionato il flag “disco di avvio”. Una volta cliccato su continua, VirtualBox ci avvertirà che, continuando senza disco, non potremo fare il boot della macchina. Il disco di boot verrà aggiunto manualmente in seguito: selezionare pertanto “continua”. Nell’ultima schermata bisogna semplicemente cliccare su “continua” per completare la creazione della VM.

E’ il momento di aggiungere il disco di avvio alla VM appena creata: entrare nelle impostazioni relative alla virtual machine e andare nella sezione “archiviazione”. Nell’albero dei controller deve essere selezionata la voce “controller ide” , cliccando sull’immagine del disco con il “+” verde in basso a sinistra, comparirà l’opzione “aggiungi disco fisso. Alla domanda successiva, relativa alla creazione di un disco vuoto, deve essere selezionata la voce “scegli disco esistente”. E’ possibile ora navigare fino alla posizione dove è stata scompattata l’immagine della virtual machine, e selezionare il primo della serie di files che compongo l’immagine, in questo caso il file si chiama “CentOS 64-bit-cl1.vmdk”, riconoscibile come primo della serie per l’assenza di numeri sequenziali nel nome.

Dopo aver aggiunto il disco la virtual machine è pronta per essere avviata. Completata la fase di boot, il sistema proverà a far partire l’ambiente grafico, ma, essendo il sistema configurato per funzionare con VmWare, i driver attualmente installati non possono funzionare con la scheda video virtuale di VirtualBox. Verrà quindi mostrata una schermata blu di errore che chiederà se devono essere visualizzati i log. Dopo aver risposto “No”, verrà mostrata un’ulteriore schermata per l’autoconfigurazione di GDM. Anche in questo caso non è quello che serve, quindi la risposta da dare è nuovamente “No”.

Si viene quindi condotti a una schermata di login su shell; per accedere l’ username è root, la password è password.

Essendo ora amministratori della macchina è possibile lanciare lo script che disinstallerà i driver specifici di VMWare: vmware-uninstall-tools.pl.

lo script eseguirà la disinstallazione completa dei driver. Una volta terminata, è necessario riavviare il sistema. Subito dopo il riavvio ci troveremo nuovamente di fronte alle domande relative al log e all’autoconfigurazione di GDM. Rifiutando come prima, sarà possibile effettuare nuovamente login da shell come root.

E’ ora possibile installare i driver di VirtualBox. Per farlo è necessario andare nel menù “dispositivi” di VirtualBox, e selezionare l’opzione “installa guest additions”. A questo punto tornando sulla shell è possibile lanciare il comando:

mount /dev/cdrom /media

Andiamo adesso nella directory media:

cd /media

e eseguiamo lo script di installazione dei driver di VirtualBox:

bash VboxLinuxAdditions.run

Purtroppo a causa del kernel troppo vecchio di questa macchina virtuale, non sarà possibile avere l’accelerazione grafica, ma adesso il sistema è pronto per funzionare.

Dopo un ultimo riavvio, il sistema sarà correttamente funzionante anche su VirtualBox, e potrete pertanto avviare i primi passi con Greenplum direttamente da questo sistema.

Ricordiamo che il presente articolo deve essere considerato solamente per scopi di valutazione delle funzionalità di Greenplum Community Edition. Per ottenere performance maggiori, si consiglia di installare Greenplum Community Edition su sistemi non virtuali.

Buon divertimento!

]]>
https://blog.2ndquadrant.it/greenplum_vmware_virtualbox/feed/ 0