PostgreSQL Point in Time Recovery Federico Campoli
Cos è IWA/HWG IWA/HWG è un Associazione professionale no profit riconosciuta leader mondiale nella fornitura dei principi e delle certificazioni di formazione per i professionisti della Rete Internet; è presente in 100 paesi, con 130 sedi ufficiali in rappresentanza di più di 165.000 associati. La sua missione Fornire programmi formativi di qualità Fornire agli associati supporto e collaborazione a livello regionale, nazionale e internazionale, nonché un marchio di affiliazione riconosciuto a livello mondiale Promuovere i principi universali di etica e di pratica professionale per tutti i professionisti della Rete Internet Fornire supporto per la definizione e lo studio di normative nei Paesi in cui è presente
Cos è IWA/HWG Partecipazioni e Attività Network: http://www.iwa iwa.it - http://webaccessibile webaccessibile.org - http://www. itlists.org - http://blog.iwa.it - http://www.skillprofiles skillprofiles.eueu Contatti: comunicazione comunicazione@iwa.it
Sommario L'integrità dei dati La directory pg_xlog Lo storage fisico a livello di blocco Lo shared buffer Il database in esercizio Il recovery d'istanza La configurazione del PITR Trappole ed insidie da evitare PITR vs pg_dump Il progetto pg_recovery 4
L'integrità dei dati La consistenza dei dati viene garantita dal modello MVCC (Multiversion Concurrency Control) che garantisce un lock più fine di quello di riga. 5
L'integrità dei dati Per garantire questo PostgreSQL opera in maniera tanto semplice quanto brutale. Ogni volta che viene operato un update in fondo alla tabella viene inserita una nuova nuova riga mentre la vecchia viene marcata come defunta. la riga defunta resta cosi' a disposizione per garantire la consistenza in lettura. 6
L'integrità dei dati Per impedire il proliferare di righe invalide le quali provocano un degrado delle prestazioni PostgreSQL ha a disposizione il VACUUM che, come il nome fa supporre, passa letteralmente l'aspirapolvere sull'oggetto indicato (o su tutto il database se non viene specificato nulla). Vacuum elimina le dead tuples e, se lanciato con la clausola analyze, aggiorna le statistiche di utilizzo degli oggetti ad uso dell'ottimizzatore. 7
La directory pg_xlog La directory pg_xlog contiene i WAL file necessari all operatività del database e sui quali vengono memorizzate le variazioni a livello di blocco. Ogni segmento WAL file è grande 16 Mb. Durante la normale attività del database quando una pagina dati viene modificata viene scritta immediatamente sul WAL e poi, successivamente, su disco. 8
Lo storage fisico a livello di blocco All'interno della directory base o nella directory di tablespace sono presenti tante directory numeriche. Ognuna di queste corrisponde ad un database presente nel cluster e il nome è mappato con il campo OID della tabella di sistema pg_database. 9
Lo storage fisico a livello di blocco Andando a verificare il contenuto si scopre che all'interno di ogni cartella di database sono presenti tanti file dal nome numerico. Anche in questo caso il nome è legato all'oid relativo alla relazione, tabella o indice che sia, ma in maniera diversa da come avviene per il database. Ogni file è composto da pagine di 8kb. 10
Lo storage fisico a livello di blocco Una pagina dati è composta da 5 sezioni fisse. È presente un header di 20 byte seguito dall area contenente i puntatori agli item, lo spazio libero, gli item e, se si tratta di una IndexPage, lo spazio speciale che serve a tracciare la tipologia di indice e il relativo metodo d'accesso. 11
Lo storage fisico a livello di blocco L header di pagina, il PageHeaderData è composto da 8 campi. I primi due, entrambi lunghi 8 byte, tengono traccia del dato memorizzato su WAL più recente collegato alla pagina. Seguono poi tre campi interi lunghi 2-byte (pd_lower, pd_upper, and pd_special). 12
Lo storage fisico a livello di blocco Seguono poi due byte dell header, pd_pagesize_version, contengono la dimensione e la versione di pagina. L'ultimo campo di 4 byte serve come hint per gestire il pruning delle dead tuples con la funzionalità HOT introdotta con la 8.3 13
Lo shared buffer Lo shared buffer e un segmento in memoria che viene allocato all avvio del processo postmaster ed è formattato come le tabelle, a pagine di 8kb, o della dimensione di pagina del DBMS se diversa da default. 14
Lo shared buffer Qualsiasi operazione di lettura o di scrittura passa attraverso lo shared buffer. 15
Il database in esercizio Viene verificato che la pagina richiesta sia gia' nello shared buffer. 16
Il database in esercizio Se non e' presente la pagina richiesta viene letta dal disco. 17
Il database in esercizio La pagina viene sottoposta a pin, ovvero bloccata in memoria. 18
Il database in esercizio La pagina viene sottoposta a unpin ovvero viene sbloccata. Se sono state apportate modifiche e sono state committate queste vengono inviate sul WAL. 19
Il database in esercizio La pagina viene sottoposta a unpin ovvero viene sbloccata. Se sono state apportate modifiche e sono state committate queste vengono inviate sul WAL. 20
Il recovery d'istanza In caso di crash dell'istanza al successivo riavvio il server analizza lo stato del server leggendo il control file pg_control che si trova nella directory $PGDATA/global. Il postmaster determina dove si trova la posizione sul wal dell'ultimo checkpoint e inizia la lettura dei blocchi del wal da quel punto in avanti che vengono applicati ai data file. Il recovery avviene quindi sempre a livello di cluster a mai di singolo database. 21
Il recovery d'istanza I campi che ci interessano, in uno scenario di crash recovery sono pd_lsn e pd_tli. pd_lsn identifica la posizione sul WAL relativa all'ultima modifica della pagina pd_tli invece tiene traccia della timeline a cui appartiene la pagina 22
Il recovery d'istanza Il campo pd_lsn determina se il blocco letto dal WAL deve andare a sovrascrivere il blocco presente su data file. Il campo pd_tli determina quale timeline seguire in caso di cluster ripristinato più volte da WAL archiviati. 23
La configurazione del PITR per configurare il PITR, bisogna attivare l archiviazione dei WAL modificando nel file postgresql.conf la riga archive_command = cp -i %p ~/tmp/pg_archive/%f </dev/null 24
La configurazione del PITR In questo modo i WAL, ognuno grande 16 Mb durante la normale attivita del database vengono archiviati nella directory specificata dal comando di archiviazione. Le modifiche a questo parametro non richiedono riavvio dell istanza ma un seplice reload dei parametri di configurazione con il comando pg_ctl reload 25
La configurazione del PITR Lanciamo poi, dal client psql il comando SELECT pg_start_backup( label ); il quale genera una label, sottoforma di file di testo nella directory $PGDATA, che indicherà a PostgreSQL la presenza di un backup a caldo 26
La configurazione del PITR A questo punto possiamo eseguire una copia inconsistente della directory data con il cluster in esecuzione nella maniera più adeguata per il nostro sistema. (cp, copy, tar, zip etc.) Nel nostro esempio: fedec@gilead:~/tmp$ tar cfz pg_data.tar.gz pg_directory/ 27
La configurazione del PITR Al termine della copia lanciamo, sempre dal client psql il comando SELECT pg_stop_backup(); il quale indica a PostgreSQL il termine della copia inconsitente e forza uno switch di xlog andando ad archiviare anche le transazioni salvate sul wal durante la copia. 28
La configurazione del PITR Da questo momento in poi il cluster potrà essere ripristinato ad un tempo specifico. 29
Il point in time recovery Immaginiamo dunque che l'attività della base dati appena salvata un utente incauto cancelli una tabella molto importante provocando il blocco di un'applicazione importantissima. http://www.soft-land.org/storie/02/story12 Dopo l'opportuna punizione corporale somministrata all'utente, per recuperare la tabella cancellata si procede in questo modo: 30
Il point in time recovery Ripristino in una locazione diversa dell'hot backup salvato in precedenza: fedec@gilead:~/restore_pitr/ tar xfz pg_data.tar.gz Modifichiamo il file postgresql.conf in modo da far partire l'istanza su di una porta diversa da quella del cluster in esercizio, come ad esempio la 5433 31
Il point in time recovery Nella directory appena scompattata da tar creiamo un file recovery.conf contenente le seguenti righe: restore_command = cp /home/fedec/tmp/pg_archive/%f "%p" Dove si trovano I WAL archiviati da cui ripristinare recovery_target_time= 2007-06-16 10:33:59 Il tempo a cui fermarsi durante la recovery, in questo caso un secondo prima del drop della tabella 32
Il point in time recovery Valorizziamo la variabile d'ambiente $PGDATA e avviamo l'istanza con pg_ctl o con il lancio diretto del postmaster. Il database andrà in modalità recovery e ripristinerà tutte le variazioni fino al tempo specificato con recovery_target_time permettendo quindi, con l'utilità pg_dump di esportare la tabella droppata e reimportarla nel database di produzione 33
Il point in time recovery fedec@gilead:~/tmp$ pg_ctl startpg_ctl: another server may be running; trying to start server anyway LOG: database system was interrupted at 2007-06-16 10:31:44 CEST LOG: starting archive recovery LOG: restore_command = "cp /home/fedec/tmp/pg_archive/%f "%p"" LOG: recovery_target_time = 2007-06-16 10:33:59+02 cp: cannot stat /home/fedec/tmp/pg_archive/00000001.history : No such file or direc LOG: restored log file "000000010000000000000006.0049C300.backup" from archive LOG: restored log file "000000010000000000000006" from archive LOG: checkpoint record is at 0/649C300 LOG: redo record is at 0/649C300; undo record is at 0/0; shutdown FALSE LOG: next transaction ID: 0/609; next OID: 24576 LOG: next MultiXactId: 1; next MultiXactOffset: 0 LOG: automatic recovery in progress LOG: redo starts at 0/649C348 cp: cannot stat /home/fedec/tmp/pg_archive/000000010000000000000007 : No such file LOG: could not open file "pg_xlog/000000010000000000000007" (log file 0, segment 7) LOG: redo done at 0/649C348 LOG: restored log file "000000010000000000000006" from archive LOG: archive recovery complete server starting fedec@gilead:~/tmp$ LOG: database system is ready 34
Trappole ed insidie da evitare Corruzione di un WAL archiviato La presenza di un blocco corrotto all'interno di un WAL archiviato determina, in fase di recovery, l'arresto del processo di recovery e la segnalazione di un messaggio d'errore e l'arresto dell'istanza PostgreSQL. Per avviare l'istanza occorre quindi impostare un target precedente il punto di corruzione nel file recovery.conf. 35
Trappole ed insidie da evitare Esaurimento dello spazio archive PostgreSQL non effettua nessun controllo sullo spazio della directory archive. In caso di esaurimento dello spazio in questa directory l'archive_command produce degli errori che vengono loggati e l'intero backupset prodotto con la copia a caldo della data directory diventa inconsistente. È quindi chiaro il fatto che un archive_command semplice come visto in precedenza non è assolutamente sicuro per garantire la protezione dei dati. 36
Trappole ed insidie da evitare Versione di PostgreSQL L'archiviazione dei WAL è da considerarsi sicura solo dalla versione 8.2 in poi. Infatti nelle versioni precedenti non esiste alcun sistema per forzare uno switch dei log ne al termine dell'hot backup avviene alcun tipo di archiviazione forzata. Per le versioni 8.0 e 8.1 la sicurezza che le ultime transazioni siano archiviate è demandata al carico del server che determina in ultima istanza se e quando avviene uno switch di WAL con conseguente archiviazione. 37
PITR vs pg_dump Analizziamo infine le due possibili modalità di backup a disposizione del DBA PostgreSQL. Il PITR, novità della versione 8, e il pg_dump presentano vantaggi e svantaggi che richiedono accortezza e, in ultima analisi, non permettono un'implementazione univoca. 38
PITR vs pg_dump Backup a livello di blocco Come visto in precedenza il PITR esegue un backup a livello di blocco. I singoli blocchi vengono salvati in segmenti da 16 Mb in un'area disco separata. Questi segmenti insieme ad una copia a caldo e quindi inconsistente, della directory data permettono il ripristino con precisione pari al secondo o al transaction id del cluster. I vantaggi di tale pratica sono molteplici. 39
impostando il parametro archive_timeout nel file PITR vs pg_dump postgresql.conf che esprime il numero di secondi oltre il quale viene eseguito uno switch di log forzato. I file sono piccoli, si trasferiscono bene in rete e non occupano spazio eccessivo. La copia della directory data può essere fatta con l'utilità tar accoppiata con un compressore come gzip permettendo un notevole risparmio di spazio. In fase di restore l'elaborazione è particolarmente rapida in quanto non avvengono operazioni a livello di statement. La restore, fermo restando il livello di controllo che PostgreSQL esercita durante questo processo, si riduce ad una mera copia di blocchi dal WAL che viene scandito al data file che viene ricoperto. Eventuali corruzioni del data file vengono azzerate dall'elaborazione del WAL in quanto il parametro zero_damaged pages resta attivo per tutto il tempo necessario al recovery d'istanza. 40
PITR vs pg_dump Gli svantaggi rispetto a tale pratica sono i seguenti. 1. In un database ad elevata attività di scrittura il numero di WAL può diventare esagerata portando al raggiungimento dei limiti del filesystem in termini di inode occupati. 2. Un singolo blocco corrotto su di un wal archiviato impedisce al processo di recovery di andare avanti e impone al cluster il ripristino al punto in cui si è trovata la corruzione. 3. Bisogna fare molta attenzione alle tablespace poichè, essendo le locazioni fisiche collegate come link simbolico nella directory pg_tblspc, si corre il rischio o di non eseguirne il backup o sconvolgere la struttura del cluster in fase di ripristino. 4. In un database a bassa attività lo switch del WAL può avvenire di rado espondendo il cluster al rischio di perdita delle transazioni non archiviate. 41
PITR vs pg_dump Ognuno di questi casi ha una pratica che in qualche modo permette di tenere sotto controllo il problema. Il problema del punto 1 si risolve con una politica di pruning dei wal da gestire con il crontab. Il problema del punto 2 si limita cercando di tenere una distanza temporale non troppo elevata tra il base backup e il cluster di produzione. Il problema del punto 3 va gestito in fase di backup salvando i link simbolici e tutte le locazioni delle tablespace mentre, in fase di restore, ripristinando i link simbolici e le locazioni in modo che il cluster possa trovare le locazioni, con i permessi giusti, nel punto indicato dai link simbolici. Il problema del punto 4, dalla versione 8.2 in poi, si risolve impostando il parametro archive_timeout nel file postgresql.conf che esprime il numero di secondi oltre il quale viene eseguito uno switch di log forzato. 42
PITR vs pg_dump Il backup a livello di statement, quello che viene eseguito dal pg_dump, è ormai consolidato quale sistema di salvataggio affidabile e flessibile. I vantaggi di tale strategia di backup sono i seguenti. Esportazione dei dati in maniera flessibile. pg_dump permette di estrarre tutto il cluster come una singola tabella con o senza dati. Esportazione in vari formati. Si va dagli statement sql fino all'archivio compresso in tempo reale. La natura ``piana`` dei dati esportati limita rischi di corruzione in quanto è sempre possibile editare il file sql a caccia di valori sballati. Durante la fase di export vengono estratte le definizioni delle tablespace. 43
PITR vs pg_dump Gli svantaggi che si hanno con l'uso di questo strumento sono i seguenti. 1. I dati sono una fotografia del momento in cui è stato lanciato il comando pg_dump. 2. Non vengono esportati i comandi per la creazione delle locazioni delle tablespace. 3. In fase di restore tutto viene elaborato come statement con tempi di completamento anche molto lunghi. 4. Bisogna porre molta attenzione al character encoding durante la fase di export e di import. 44
PITR vs pg_dump Riguardo al punto 1 non c'è molto su cui operare. Il programma lavora in questo modo e bisogna giocoforza adattarsi. Il punto 2 può essere aggirato estraendo in un file SQL i comadi specifici del sistema operativo per la creazione delle locazioni. \t SELECT 'mkdir ' spclocation FROM pg_tablespace WHERE spcname NOT IN ('pg_default','pg_global'); SELECT 'chown postgres:postgres ' spclocation ' -R' FROM pg_tablespace WHERE spcname NOT IN ('pg_default','pg_global'); 45
PITR vs pg_dump Il punto 3 va affrontato con la pazienza del performance tuner. I parametri del file postgresql.conf che influenzano pesantemente le prestazioni durante sono, in linea di massima fsync e maintenance_work_mem. Il punto 4 può essere risolto con un banale SET client_encoding o rappresentare un problema spinoso, da trattare ad esempio con iconv, a seconda delle informazioni che si hanno riguardo al database originariamente esportato. La codifica più perniciosa, fonte di ogni male, resta comunque SQL_ASCII che di fatto accetta qualsiasi tipo di carattere ma lo tratta in maniera imprevedibile che viene definita volta per volta dal client encoding del backend che si sta collegando. 46
PITR vs pg_dump Implementazione doppia A questo punto è chiaro che entrambi gli approcci presentano delle caratteristiche che garantiscono magari ottime prestazioni e granularità di ripristino a scapito della sicurezza e della flessibilità di intervento sui dati esportati. L'arma vincente in questo dilemma è sostanzialmente un compromesso tra le due strategie che permetta di avere un dump sicuro dei dati eseguito con pg_dump e, successivamente un hot backup con il quale poter porre rimedio nel breve periodo a situazioni catastrofiche come la drop di una tabella o il crash del server. Una soluzione valida è ad esempio uno scenario in cui il cluster, in modalità archive, viene esportato ogni giorno su file insieme alle definizioni delle tablespace eventuali e successivamente viene operato un hot backup con salvataggio del dump, dell'hot backup e dei WAL archiviati in una locazione sicura diversa dalla macchina di produzione. 47
pg_recovery Progetto open source per l'automazione del processo di backup e restore. Python, shell script, sql Release iniziale per linux Pubblicato con licenza GNU/GPL su pgfoundry http://pgfoundry.org/projects/pgrecovery/ Stato attuale: disegno del database repository e struttura binari Due sviluppatori 48
DOMANDE 49
Termini e licenza del documento Quest'opera è stata rilasciata sotto la licenza Creative Commons Attribuzione-Non commerciale-non opere derivate 3.0 Unported. Per leggere una copia della licenza visita il sito web http://creativecommons.org/licenses/by-nc-nd/3.0/ o spedisci una lettera a Creative Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA. Diritti, marchi registrati e siti web riportati in immagini e url sono riservati e proprietà dei diretti interessati e relative aziende. IWA/HWG e l associazione IWA Italy non sono direttamente o indirettamente responsabili dei contenuti riportati nel presente documento che sono ad esclusiva cura e responsabilità del relatore.
PostgreSQL Point in time recovery Strategie di backup con recupero dei dati al tempo specificato 51