Introduzione a PostgreSQL
(il piu' avanzato database OpenSource al mondo)
Ing. Luca Ferrari, PhD
[email protected]
Italian PostgreSQL Users Group
ITPug
Synopsis
Questa presentazione vuole introdurre
PostgreSQL, il piu' avanzato database OpenSource
al mondo. Verranno trattati diversi temi, fra i quali
i principali sono:
La comunità internazionale e nazionale
La storia del progetto, lo stato attuale e gli sviluppi
futuri
Installazione e utilizzo di base del sistema
Accenni al funzionamento interno
Transazioni
Window Functions e CTE
Cool Features
Partitioning
Introduzione a PostgreSQL e ai database – Luca Ferrari
2 di 139 Database
Un database è un contenitore di dati
(generalmente strutturati).
Perché occorre usare un database?
Principio di delega: il database si occupa solo dei dati, e
li gestisce nel modo migliore indipendentemente dalle
applicazioni che ne hanno bisogno.
Fruibilità: usando un database i dati vengono svincolati
dalla applicazione, e quindi i dati possono essere
accessibili da diverse applicazioni.
Introduzione a PostgreSQL e ai database – Luca Ferrari
3 di 139 Vantaggi nell'uso di un database
Il database si occupa della gestione degli accessi
allo spazio di memorizzazione (disco)
Il database organizza i dati in modo da garantire il
miglior accesso agli stessi
Il database mantiene i dati coerenti (non significa
corretti, ma non sbagliati)
Il database gestisce gli accessi concorrenti
Il database consente backup, ripristino,
replicazione, …
Non si reinventa la ruota!
Introduzione a PostgreSQL e ai database – Luca Ferrari
4 di 139 Tipologie di database
I database possono essere classificati in
base alle loro caratteristiche in diversi
insiemi ortogonali:
Relational Database
Object Database
Embedded (es. BerkelyDB, Derby)
Server (es. MySQL, PostgreSQL)
Document Oriented (CouchDB, Haddop)
...
Introduzione a PostgreSQL e ai database – Luca Ferrari
5 di 139 Database maggiormente affermati
I database maggiormenti affermati sono
quelli di tipo relazionale, con tuple
disposte per righe (esistono anche
database per colonne!).
Il linguaggio (dichiarativo) standard per
l'interazione con tali database è lo
Structured Query Language (SQL).
Introduzione a PostgreSQL e ai database – Luca Ferrari
6 di 139 Database relazionali
I dati sono correlati fra loro sulla base del
loro valore.
I Relational Database (RDBMS) sfruttano
le foreign keys (chiavi esterne) per
imporre i vincoli di integrità referenziali.
Ovviamente una chiave esterna deve identificare
univocamente una tupla nell'entità referenziata.
Introduzione a PostgreSQL e ai database – Luca Ferrari
7 di 139 Introduzione a PostgreSQL
Introduzione a PostgreSQL e ai database – Luca Ferrari
8 di 139 Introduzione a PostgreSQL
PostgreSQL è un ORDBMS (Object Relational
Database
Management
System)
di
classe
enterprise
E' un prodotto Open-Source mantenuto da un
team mondiale di sviluppatori ed esperti
E' conforme agli standard SQL (SQL 2003)
supportandone
caratteristiche
sintattiche
e
semantiche
Introduzione a PostgreSQL e ai database – Luca Ferrari
9 di 139 Storia
Il prof. Michael Stonebreaker dell'University of Berkely crea un database denominato
Ingres. Lascia poi l'università per creare un'azienda che si occupi del business
Ingres. Successivamente torna all'università con l'intenzione di creare un nuovo
database, sfruttando quanto imparato dal progetto Ingres.
1986:
1986 il prof. Stonebreaker è a capo di un progetto finanziato da DARPA e
ARO per la creazione di POSTGRES;
1988:
1988 la prima versione di POSTGRES;
1990:
1990 POSTGRES v.2
1991:
1991 POSTGRES v.3
1994:
1994 Andrew Yu e Jolly Chen aggiungono il supporto per l’SQL; il pacchetto
viene rilasciato con il nome di POSTGRES95;
1996:
1996 continua lo sviluppo, si cambia nome in POSTGRESQL v.6;
…
2006:
2006 viene rilasciata la versione 8.2
2007:
2007 primo PGDay in Italia (international)
2008:
2008 viene rilasciata la versione 8.3
secondo PGDay in Italia (international) - ITPUG
2009:
2009 viene rilasciata la versione 8.4
terzo PGDay in Italia (National)
2010:
2010 viene rilasciata la versione 9.0
Introduzione a PostgreSQL e ai database – Luca Ferrari
10 di 139 La nascita del Core Team
La nascita del Core Team
Date: Mon, 08 Jul 1996 22:12:19­0400 (EDT) Date: Mon, 08 Jul 1996 22:12:19­0400 (EDT) From: "Marc G. Fournier" <[email protected]>
From: "Marc G. Fournier" <[email protected]>
Subject: [PG95]: Developers interested in improving PG95?
Subject: [PG95]: Developers interested in improving PG95?
To: Postgres 95 Users <[email protected]>
To: Postgres 95 Users <[email protected]>
Hi... A while back, there was talk of a TODO list and development moving forward on Hi... A while back, there was talk of a TODO list and development moving forward on Postgres95 ... at which point in time I volunteered to put up a cvs archive and sup server Postgres95 ... at which point in time I volunteered to put up a cvs archive and sup server so that making updates (and getting at the "newest source code") was easier to do...
so that making updates (and getting at the "newest source code") was easier to do...
... Just got the sup server up and running, and for those that are familiar with sup, the ... Just got the sup server up and running, and for those that are familiar with sup, the following should work (ie. I can access the sup server from my machines using this):
following should work (ie. I can access the sup server from my machines using this):
Thomas Lockhart (ritirato), Jan Wieck, Tom Lane, Marc Fournier
Vadim Mikheev, Bruce Momjian
Introduzione a PostgreSQL e ai database – Luca Ferrari
11 di 139 Caratteristiche principali
Modello client-server: i dati sono gestiti in modo centralizzato
(server) e messi a disposizione di più fruitori (client)
Avanzato supporto per lo standard SQL2003: garantisce la
portabilità di applicazioni
Concorrenza e Multi-utenza: supporta connessioni e query multiple
contemporaneamente
Integrità referenziale: garantisce la coerenza dei dati rispetto alle
business rules
Transazioni: esecuzione di più istruzioni in modo coerente (ACID
rules)
Sub-query: possibilità di innestare le query
Indici: diverse modalità di accesso rapido ai dati (anche per
controllo di coerenza)
Replica: possibilità di sincronizzare due o più istanze fra loro
Introduzione a PostgreSQL e ai database – Luca Ferrari
12 di 139 Caratteristiche principali
Query planner: le query sono analizzate per individuare il miglior
percorso di accesso ai dati
Triggers: reazione agli eventi di inserimento, cancellazione,
aggiornamento di tuple
Rules: possibilità di ordinare la riscrittura di query
Stored procedures: funzioni memorizzate presso il server
Ereditarietà: relazione padre-figlio fra le tabelle
Sequenze: produzione di valori auto-incrementati
Tipi di dato: possibilità di definire un tipo di dato personalizzato
Tablespaces: controllo sullo storage fisico
Partizionamento: possiblità di separare i dati e organizzarli secondo
le esigenze fisiche di storage
Connettività da diversi linguaggi: Perl, Java, C, Python, Ruby,
PHP, ...
Linguaggi integrati: PL/Perl, PL/Java, PL/PGSQL, …
Common Table Expressions: CTE (clausola WITH)
Introduzione a PostgreSQL e ai database – Luca Ferrari
13 di 139 Licenza BSD­like
PostgreSQL Database Management System
(formerly known as Postgres, then as Postgres95)
Portions Copyright (c) 1996­2010, The PostgreSQL Global Development Group
Portions Copyright (c) 1994, The Regents of the University of California
Permission to use, copy, modify, and distribute this software and its documentation for any purpose, without fee, and without a written agreement is hereby granted, provided that the above copyright notice and this paragraph and the following two paragraphs appear in all copies.
IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
Introduzione a PostgreSQL e ai database – Luca Ferrari
14 di 139 Un problema di diffusione?
It is a simple, permissive non­copyleft free software license, compatible with the GNU GPL.
We urge you not to use the original
BSD license for software you write.
Introduzione a PostgreSQL e ai database – Luca Ferrari
15 di 139 http://www.postgresql.org
Introduzione a PostgreSQL e ai database – Luca Ferrari
16 di 139 http://www.planetpostgresql.org
Esiste anche il planet italiano: http://www.planetpostgresql.it !
Introduzione a PostgreSQL e ai database – Luca Ferrari
17 di 139 http://pgfoundry.org
Introduzione a PostgreSQL e ai database – Luca Ferrari
18 di 139 http://www.itpug.org
Users' Group Italiano di PostgreSQL
Fornisce un front­end per le aziende e le istituzioni che vogliano usare PostgreSQL.
Si occupa della diffusione di PostgreSQL e della sua ideologia.
Definisce una serie di chapter regionali per la localizzazione sul territorio.
Introduzione a PostgreSQL e ai database – Luca Ferrari
19 di 139 http://www.pgday.it
Introduzione a PostgreSQL e ai database – Luca Ferrari
20 di 139 PostgreSQL Weekly News
Introduzione a PostgreSQL e ai database – Luca Ferrari
21 di 139 Community
Introduzione a PostgreSQL e ai database – Luca Ferrari
22 di 139 Installazione,
concetti base
e primo utilizzo
Introduzione a PostgreSQL e ai database – Luca Ferrari
23 di 139 Numeri di versione
8.4.4
Il Il primo primo numero numero è è un un major major number
number e e indica il “settore di mercato” in cui si indica il “settore di mercato” in cui si colloca la release.
colloca la release.
Ad esempio la 7.x.x era denominata no­
Ad esempio la 7.x.x era denominata no­
crash, la 8.x.x era quella con il port crash, la 8.x.x era quella con il port Windows.
Windows.
Il terzo numero è un minor Il terzo numero è un minor number e indica il patch level number e indica il patch level della release.
della release.
Richiede un nuovo initdb.
Non richiede initdb.
Il secondo numero è un major number e Il secondo numero è un major number e indica il numero annuale di release.
indica il numero annuale di release.
Deve Deve essere essere combinato combinato con con il il primo primo numero numero per per fare fare riferimento riferimento ad ad una una release specifica.
release specifica.
Introduzione a PostgreSQL e ai database – Luca Ferrari
24 di 139 Installazione
Su Linux distribuzioni Debian-like:
apt­get install postgresql
apt­get install postgresql­doc
apt­get install postgresql­contrib
Potrebbe essere necessario inizializzare la directory che conterrà i dati con initdb.
Su *BSD:
cd /usr/ports/databases/postgresql84server && make install
E' ovviamente possibile compilare dai sorgenti,
per avere un ambiente ottimizzato.
Il demone PostgreSQL gestisce un cluster di
database, ovvero un insieme di più database
possono risiedere sullo stesso host e possono
essere gestiti dallo stesso processo.
Introduzione a PostgreSQL e ai database – Luca Ferrari
25 di 139 template1, template0
Appena installato il cluster mette a disposizione
un database vuoto, usato come template per la
creazione di altri database: template1.
Tutte le caratteristiche di template1 possono
essere modificate dall'utente, e saranno riflesse in
ogni nuovo database creato.
Il database template0 rappresenta una copia di
sicurezza di template1.
Introduzione a PostgreSQL e ai database – Luca Ferrari
26 di 139 Connessione al database: psql
Si può usare il client testuale psql:
In modo analogo ad una shell Unix, il simbolo # identifica un prompt di
amministratore, mentre un carattere > un prompt di utente normale.
Introduzione a PostgreSQL e ai database – Luca Ferrari
27 di 139 Password prompt degli strumenti testuali
Tutti gli strumenti testuali (psql, pg_dump, pg_dumpall, …) leggono un file particolare
$HOME/.pgpass che puo' contenere l'associazione
server, database, username e password.
<server>:<port>:<database>:<username>:<password>
dbserver:5432:raydb:luca:XXX
dbserver:5432:ordinidb:luca:XXX
backupserver:5432:*:luca:XXX
Se viene trovata l'associazione, allora la password
non viene chiesta al prompt dei comandi (comodo per
l'automazione di script).
Introduzione a PostgreSQL e ai database – Luca Ferrari
28 di 139 Connessione al database: pgadmin3
pgadmin3 è un client grafico basato sulle librerie wxWidgets, grazie
alle quali funziona su piattaforma Linux, Unix, Microsoft, Apple
Introduzione a PostgreSQL e ai database – Luca Ferrari
29 di 139 Connessione al database: phpgadmin
Client Web basato su PHP
Introduzione a PostgreSQL e ai database – Luca Ferrari
30 di 139 Creazione di un nuovo utente
Una volta collegati al database template1 è possibile creare un
nuovo utente tramite CREATE USER
E' possibile creare l'utente da una shell usando il comando
create_user
Introduzione a PostgreSQL e ai database – Luca Ferrari
31 di 139 Creazione di un database
Una volta collegati al database template1 è possibile
creare un nuovo database con CREATE DATABASE.
Introduzione a PostgreSQL e ai database – Luca Ferrari
32 di 139 Creazione di un database
Si può anche usare lo script createdb direttamente da
shell.
Introduzione a PostgreSQL e ai database – Luca Ferrari
33 di 139 Creazione di una semplice tabella
Ci si collega al database linuxdaydb e si impartiscono i
comandi necessari: CREATE TABLE
Si noti la creazione dell'indice sulla chiave primaria e sul vincolo unique.
Viene inoltre creata una sequenza in automatico per il campo serial.
Introduzione a PostgreSQL e ai database – Luca Ferrari
34 di 139 Popolamento del database
Introduzione a PostgreSQL e ai database – Luca Ferrari
35 di 139 Introspezione
I comandi \ di psql consentono di effettuare
introspezione sulla struttura del database. Ad
esempio:
\d fornisce l'elenco degli oggetti nel database
\d nome_tabella fornisce la struttura di una
tabella
\df fornisce l'elenco delle stored procedure
\df nome_funzione fornisce il codice sorgente
della funzione
Introduzione a PostgreSQL e ai database – Luca Ferrari
36 di 139 Introspezione: esempio
Introduzione a PostgreSQL e ai database – Luca Ferrari
37 di 139 Cluster di database
PostgreSQL è un demone che gestische un cluster di database.
E' possibile vedere i database che una istanza gestisce mediante i
comandi \list (da prompt psql) o flag -l da linea di comando psql.
Introduzione a PostgreSQL e ai database – Luca Ferrari
38 di 139 postgresql.conf
Il file postgresql.conf contiene parametri fondamentali
configurazione del server. Alcuni interessanti sono:
per
la
listen_address = 'localhost' {*, ip address}
specifica per quali interfacce il server deve accettare connessioni
log_statement = 'none'
{none, all, ddl, mod}
consente di abilitare il logging dei comandi SQL eseguiti dal server,
utile per il debugging o il monitoring dell'attività del server
shared_buffers = 24MB
{almeno 16k a connessione}
indica la memoria disponibile per PostgreSQL in cui conservare le
pagine dati
work_mem = 1MB
è la memoria usata per il sorting (clausole ORDER BY)
Introduzione a PostgreSQL e ai database – Luca Ferrari
39 di 139 postgresql.conf: ottimizzatore e costi
# Definiscono quali metodi l'ottimizzatore può usare per l'accesso ai dati
# (ad esempio si potrebbe voler impedire di usare il seqscan...)
enable_bitmapscan = on
enable_hashagg = on
enable_hashjoin = on
enable_indexscan = on
enable_mergejoin = on
enable_nestloop = on
enable_seqscan = on
enable_sort = on
enable_tidscan = on
# Costi (relativi) di accesso alle operazioni. Sostanzialmente sono # espressi in una scala arbitraria, si noti che il costo della pagina
# è superiore a quello di un'operazione di CPU (es. sorting)
seq_page_cost = 1.0 # measured on an arbitrary scale
random_page_cost = 4.0 # same scale as above
cpu_tuple_cost = 0.01 # same scale as above
cpu_index_tuple_cost = 0.005 # same scale as above
Introduzione a PostgreSQL e ai database – Luca Ferrari
40 di 139 pg_hba.conf
Il file pg_hba.conf (host base access) contiene le regole
per l'accesso al server PostgreSQL da parte dei client della
rete. Occorre specificare il database, la maschera di rete
dei client (o l'indirizzo ip) e il metodo di accesso (trust,
md5,...):
# TYPE DATABASE USER CIDR­ADDRESS METHOD
local all all trust
host all all 127.0.0.1/32 md5
host linuxdaydb linux 192.168.1.0/24 md5
host all all ::1/128 md5
Introduzione a PostgreSQL e ai database – Luca Ferrari
41 di 139 Monitorare i processi
PostgreSQL usa uno schemi a processi; oltre al server
(chiamato postmaster) esiste un processo per ogni
connessione utente. Unitamente vi sono dei processi per il
WAL, per il BGWRITER e per lo STATISTIC COLLECTOR.
Introduzione a PostgreSQL e ai database – Luca Ferrari
42 di 139 Monitorare le query in esecuzione
E' possibile interrogare il catalogo di sistema
visualizzare le attività in corso nel database:
per
In alternativa ogni processo backend collegato ad un client
compare nella lista processi con il nome di postgres e ha una
descrizione che rappresenta l'attività del processo in quel
momento.
Introduzione a PostgreSQL e ai database – Luca Ferrari
43 di 139 Statistiche usate dall'ottimizzatore
La pseudo-tabella pg_stats contiene le informazioni sui
valori che compaiono nelle colonne di una tabella. L'indice
valuta questi valori, il loro istogramma e decide se vale la
pena usare l'indice per la clausola di selezione indicata.
Introduzione a PostgreSQL e ai database – Luca Ferrari
44 di 139 Creazione di tipi personalizzati
E' possibile creare dei tipi di dato personalizzati, in modo
da rendere più strutturata la definizione di una tabella. I
tipi possono essere usati in ogni oggetto del database.
Introduzione a PostgreSQL e ai database – Luca Ferrari
45 di 139 Creazione di array
E' possibile inserire in una tabella un array di tipi (anche
personalizzati) usando il costrutto []. Gli array possono anche
diventare multi-dimensionali.
L'indice dell'array parte sempre da 1!
Introduzione a PostgreSQL e ai database – Luca Ferrari
46 di 139 Selezione/update dei valori di un array
Introduzione a PostgreSQL e ai database – Luca Ferrari
47 di 139 Funzionamento interno
Introduzione a PostgreSQL e ai database – Luca Ferrari
48 di 139 Layout fisico
Tutti gli oggetti del database si trovano nella directory
$PGDATA. Al suo interno vi sono diverse sotto-directory:
base: contiene tutti i database e gli oggetti del
database
pg_clog: contiene lo stato delle transazioni in corso
pg_xlog: contiene i log WAL
pg_twophase: contiene le informazioni sul 2-ph commit
pg_tblspc: contiene dei link ai tablespace definiti
Introduzione a PostgreSQL e ai database – Luca Ferrari
49 di 139 Layout fisico: identificare gli oggetti
Dal catalogo di sistema è possibile identificare i vari oggetti su disco:
Introduzione a PostgreSQL e ai database – Luca Ferrari
50 di 139 Funzionamento interno
Ad
ogni
connessione
il
processo
postmaster effettua una fork e crea un
processo postgres (backend) per gestire
la connessione.
Il
parser
analizza
la
query
sintatticamente
Il Traffic Cop decide se il comando è
standard
o
di
manutenzione
(es.
vacuum).
Il rewriter considera le regole e
riscrive la query corretta (es. query su
una vista). L'ottimizzatore, tramite un
algoritmo genetico, calcola il percorso
migliore.
L'executor esegue la query.
Introduzione a PostgreSQL e ai database – Luca Ferrari
51 di 139 Shared Memory
Tutti i processi postgres condividono una zona di memoria (shared memory)
che contiene le pagine dati caricate.
Introduzione a PostgreSQL e ai database – Luca Ferrari
52 di 139 Transazioni
Introduzione a PostgreSQL e ai database – Luca Ferrari
53 di 139 Livelli di isolamento delle transazioni
PostgreSQL supporta i quattro livelli di isolamento standard, ma
internamente implementa tutto con solo due livelli: serializable e read
committed (il default). La ragione di questa scelta implementativa è da
ricercarsi nell'uso di MVCC!
Livello di isolamento
Read uncommitted Read committed Repeatable read Serializable Dirty Read
Nonrepeatable Read
Phantom Read
Supportato in PostgreSQL
Possibile
Impossibile
Impossibile
Impossibile
Possibile
Possibile
Impossibile
Impossibile
Possibile
Possibile
Possibile
Impossibile
NO
SI
NO
SI
Dirty Read: si leggono dati non ancora committati da un'altra transazione
Nonrepeatable read: dati letti in precedenza sono ora modificati dal commit di un'altra transazione.
Phantom Read: il set di dati che soddisfa una certa condizione è ora cambiato.
Read Committed: ogni comando vede uno snapshot dei dati già committati prima dell'inizio del comando stesso. Non sono visibili tuple committate da transazioni concorrenti se il co0mmit avviene durante il comando stesso. Nel caso di update concorrente la transazione attende la fine delle altre transazioni
e rivaluta la riga prima di procedere.
Serializable: ogni transazione agisce emulando la serializzazione, ossia vede solo i dati che hanno subito commit all'inizio della transazione. Lo snapshot in questo caso non cambia per nessun comando della transazione.
Read Committed → isolamento a livello di comando
Serializable → isolamento a livello di transazione
Introduzione a PostgreSQL e ai database – Luca Ferrari
54 di 139 Transazioni
Le transazioni sono attivate con i classici comandi BEGIN e
COMMIT/ROLLBACK; le transazioni possono essere
innestate (sottotransazioni).
Anche
i
SAVEPOINT
sono
implementati
come
sottotransazioni.
Livello Base (gestione di un singolo comando)
StartTransactionCommand
CommitTransactionCommand
AbortTransactionCommand
Livello Utente (gestione BEGIN/END)
BeginTransactionBlock
EndTransactionBlock
UserAbortTransactionBlock
DefineSavepoint/RollbackSavepoint
ReleaseSavepoint
Livello Gestione (backend)
StartTransaction/CommitTransaction/AbortTransaction
StartSubTransaction/CommitSubTransaction/AbortSubTransaction
Introduzione a PostgreSQL e ai database – Luca Ferrari
55 di 139 Transazione e Savepoint: esempio
Lo stato di una transazione (committed, in progress, aborted, subcommitted) viene
memorizzato su disco all'interno della directory pg_clog.
Introduzione a PostgreSQL e ai database – Luca Ferrari
56 di 139 Numerazione delle transazioni
Le transazioni seguono la numerazione
delle tuple: ogni transazione è identificata
da un numero intero positivo e progressivo
denominato XID.
Lo XID è un intero a 4 byte (32 bit).
L'età di una transazione è stabilita in modo
relativo rispetto ad un'altra transazione:
uno XID minore indica una transazione
iniziata prima e quindi piu' vecchia.
Introduzione a PostgreSQL e ai database – Luca Ferrari
57 di 139 Wraparound
In un sistema con molte transazioni si puo' assistere ad un
wraparound: due transazioni finiscono con l'avere lo stesso XID,
e quindi non possono piu' essere comparate!
Considerando un bit di segno, questa eventualità si verifica ogni
231 transazioni, superato il quale si ha una inversione di segno e
le transazioni piu' vecchie appaiono come piu' giovani!
Siccome le transazioni “nel futuro” non possono essere visibili
(READ_COMMITTED), al wraparound si ottiene la perdita dei
dati!
Vacuum analizza l'età di ogni tupla (XMIN), se questo è
maggiore di una soglia vicina al wraparound (min_freeze_age)
allora resetta lo XMIN ad un valore di default FROZENXID.
FROZENXID è l'id piu' vecchio presente nel sistema (valore
numerico 3), così da apparire sempre nel passato e non essere
piu' sottoposto a Vacuum.
Introduzione a PostgreSQL e ai database – Luca Ferrari
58 di 139 Quanto manca al wraparound?
La funzione age(..) fornisce l'età di una tupla,
ovvero quante transazioni fa la tupla è stata
aggiornata.
Il parametro min_freeze_age determina dopo
quante transazioni si deve forzare un Vacuum.
Introduzione a PostgreSQL e ai database – Luca Ferrari
59 di 139 Transazioni e log: pg_clog
Il log pg_clog memorizza (in modo permanente) lo stato
delle transazioni avviate (ed eventualmente completate)
nel sistema. Le transazioni possono essere in uno dei
seguenti quattro stati (quindi richiedendo 2 bit):
In progress (transazione avviata ma non completata)
Committed (transazione completata con successo)
Aborted (transazione completata con rollback forzato o
manuale)
Sub­committed (sottotransazione/savepoint passato con
successo)
I dati di pg_clog sono mappate in memoria, così da accelerare l'accesso ai
dati. L'unico caso in cui si richiede di rileggere lo stato da disco è per una
transazione idle da diverso tempo.
Introduzione a PostgreSQL e ai database – Luca Ferrari
60 di 139 pg_clog in dettaglio
00
01
11
00
Pagina pg_clog
00
01 11
00
00
01
11
00
Pagina pg_clog
Le transazioni sono memorizzate secondo l'ordine dato dal loro XID (identificativo del numero di transazione). Ne consegue che, sapendo la dimensione della pagina dati, sapendo che il blocco di una singola transazione è lungo 2 bit, e sapendo l'id della transazione è possibile risalire al dato circa il suo stato. Si tenga presente che in un byte ci sono le informazioni di 4 transazioni (1 byte = 2 bit * 4).
n_pagina_pg_clog = ceiling( XID / ( page_size * 4 ) )
byte_interno_alla_pagina = round( XID mod( page_size * 4 ) / 4 ) offset_nel_byte = ( XID mod 4 ) ­1
Qualora lo stato di transazione non sia già in cache, la lettura da disco (funzione TransactionLogFetch) mette in cache il dato. Solitamente si tiene solo uno stato in cache (ossia una sola porzione di log), viceversa la scrittura dello stato di una transazione (functione TransactionLogUpdate) può accettare anche piu' XID contemporaneamente ( ad esempio transazioni annidate).
Introduzione a PostgreSQL e ai database – Luca Ferrari
61 di 139 Locking
PostgreSQL supporta diversi tipi di locking:
Share: prevalentemente comandi di lettura (che
possono anche aggiornare)
ACCESS SHARE (pura lettura)
ROW SHARE (lettura/modifica, es. SELECT FOR UPDATE)
Exclusive: scrittura o alterazione della struttura
ACCESS EXCLUSIVE (alterazione, ALTER TABLE)
ROW EXCLUSIVE (scrittura, INSERT/UPDATE)
Introduzione a PostgreSQL e ai database – Luca Ferrari
62 di 139 Multi Version Concurrency Control
Introduzione a PostgreSQL e ai database – Luca Ferrari
63 di 139 MVCC
PostgreSQL utilizza il Multi-Version Concurrency Control
per implementare l'isolamento nelle transazioni.
Concettualmente ogni transazione vede uno snapshot del
database e agisce su quello snapshot.
Lo snapshot è costruito considerando solo le transazioni
precedenti la corrente che hanno effettuato il commit.
Presuppone un livello di isolamento read-committed.
E' concettualmente simile alle tecniche COW (Copy On
Write, come ad esempio ZFS).
Introduzione a PostgreSQL e ai database – Luca Ferrari
64 di 139 MVCC: implementazione
Internamente il database deve registrare non solo le
informazioni utente (i dati veri e propri) ma anche i metadati per
la gestione dell'MVCC.
Ogni tupla viene estesa con 4 campi particolari, usati per i
controlli del sistema MVCC:
xmin: indica il minimo valore di transazione che ha accesso ai
dati (sostanzialmente la transazione che ha creato la tupla)
xmax: indica il massimo valore di transazione che ha accesso
ai dati (sostanzialmente l'id dell'ultima transazione che ha
cancellato/modificato la tupla)
cmin: indica il minimo valore di comando che ha accesso ai
dati
cmax: indica il massimo valore di transazione che ha accesso
ai dati
Introduzione a PostgreSQL e ai database – Luca Ferrari
65 di 139 MVCC: esempio
Sostanzialmente il sistema controlla se la tupla è ancora visibile per
l'id di transazione corrente
(xmin <= transaction_id <= xmax)
&& (xmin committed) && (! (xmax committed || locked))
e se è visibile per il comando corrente all'interno delle transazione
(cmin <= command_id <= cmax)
&& (xmin committed || xmin == current_transaction)
&& (! (xmax committed || locked)) Il test è in realtà più complesso !
(lock, xmax nullo se abort, commit)
Introduzione a PostgreSQL e ai database – Luca Ferrari
66 di 139 MVCC: approccio visuale
xmin
cmin
10
18
21
xmax cmax
20
talkspk
talksid
1 pg01
2 pg02
3 pg03
descrizione
PostgreSQL
PostgreSQL/Python
PostgreSQL/JDBC
docente
Luca Ferrari
Luca Ferrari
Luca Ferrari
La riga con xmin e xmax impostati ha subito una delete (o un
update) e quindi non è più valida. Tutte le transazioni
successive alla numero 20 non potranno vedere quella riga, che
però esiste ancora nel database.
Problema:
il database rimane pieno di righe non più valide!
Introduzione a PostgreSQL e ai database – Luca Ferrari
67 di 139 Vacuum
Il sistema esegue (manualmente o automaticamente) un comando di
utilità, denominato vacuum, che controlla una tabella o un database alla
ricerca delle tuple che non sono più visibili da nessuna transazione (ossia
che hanno un xmax inferiore al prossimo id di transazione) e le cancella. In
questo modo i dati si ricompattano e il database riduce il suo spazio.
Introduzione a PostgreSQL e ai database – Luca Ferrari
68 di 139 Vacuum
L'esecuzione di vacuum è un'operazione pesante, perché il
sistema deve acquisire un lock esclusivo sull'oggetto.
Vacuum può essere usato per eliminare le tuple espirate
(vacuum full), oppure per aggiornare le statistiche di
sistema (vacuum analyze).
Ogni volta che vacuum esegue, lo xmin di tutte le tuple
visibili viene resettato (freeze) così da prevenire problemi ad
un successivo wrap-around.
Può essere abilitato di default nel file postgresql.conf:
autovacuum = on
Introduzione a PostgreSQL e ai database – Luca Ferrari
69 di 139 Vacuum: esecuzione
Introduzione a PostgreSQL e ai database – Luca Ferrari
70 di 139 Informazioni di visibilità
Un'operazione di Vacuum sposta e compatta
le tuple, arrivando anche a cancellare quelle
espirate.
Come ci si comporta con l'indice che puntava
a quelle tuple?
L'indice non contiene il riferimento alla tupla ma
solo alla pagina dati → le tuple cancellate
(espirate) non comportano modifiche all'indice
Le tuple spostate da Vacuum comportano un
nuovo aggiornamento dell'indice.
Introduzione a PostgreSQL e ai database – Luca Ferrari
71 di 139 HOT – Heap Only Tuples
A partire dalla versione 8.3 il sistema consente anche il
microvacuum, ovvero un vacuum limitato ad una sola
pagina dati.
Quando viene aggiornata una tupla, e l'aggiornamento
non necessità di una modifica all'indice (ossia si
modificano solo le colonne fuori dagli indici):
La tupla vecchia viene marcata come expired;
La nuova tupla viene inserita;
La tupla vecchia contiene un puntatore alla tupla nuova.
Seguendo i link si trova rapidamente la
tupla piu' ricente nella pagina dati.
Introduzione a PostgreSQL e ai database – Luca Ferrari
72 di 139 MicroVacuum
Il sistema può operare un vacuum ridotto
(microvacuum) all'interno della pagina
dati, rimuovendo le tuple expired e i
relativi link alla tupla piu' recente.
In questo modo la pagina dati rimane
sempre “ordinata” e non c'è bisogno di
grossi carichi per un vacuum globale.
Introduzione a PostgreSQL e ai database – Luca Ferrari
73 di 139 WAL
Il Write Ahead Log mantiene le informazioni sull'andamento delle
transazioni. E' fisicamente archiviato all'interno della directory
pg_xlog, e a differenza dei log in pg_clog non mantiene solo lo stato
finale di una transazione, ma anche le informazioni (progressive) sui
dati “toccati”.
La teoria vuole che il WAL sia scritto prima dei dati, ma questo
produrrebbe un sovraccarico di scritture.
Per ovviare al problema PostgreSQL divide i WAL log in segments
(LSN). A questo punto i log WAL sono mantenuti in memoria, e ogni
volta che si scrive una pagina di dati “sporca” si fa precedere la
scrittura dalla scrittura di tanti segmenti di log fino al
raggiungimento del numero LSN corrispondente alla pagina.
Analogamente, durante un recovery tutte le transazioni con XID
inferiori al LSN che si sta attualmente analizzando sono da
considerarsi stabili (ripristinate completamente).
Introduzione a PostgreSQL e ai database – Luca Ferrari
74 di 139 WAL: funzionamento in breve
La scrittura del WAL avviene secondo lo schema seguente:
1) acquisizione di un PIN e del lock sulla pagina dati da
modificare (pin indica che la pagina non deve essere scartata
dalla memoria)
2) modifica della pagina di dati
3) la pagina viene marcata come dirty
4) si richiama xloginsert per inserire la pagina di log e si
imposta il LSN nella pagina dati al valore risultante dall'insert
Le pagine WAL contengono modifiche in modo incrementale (delta).
Problema: ci si fida delle scritture del sistema operativo? Il sistema
mantiene dei checkpoints, ossia ogni n transazioni forza una scrittura di
una pagina dati completa nel WAL: alla prima modifica di una pagina sotto
la condizione
LSN < checkpoint si scrive la pagina dati completa nel WAL.
Introduzione a PostgreSQL e ai database – Luca Ferrari
75 di 139 Log in azione!
Introduzione a PostgreSQL e ai database – Luca Ferrari
76 di 139 Come viene trattata una query?
Nel file di configurazione è possibile abilitare dei
parametri per visualizzare come una singola query
viene trattata.
La query viene tradotta in un AST (Abstract
Syntax Tree)
Vengono risolti gli alias e la query viene riscritta
La query viene passata all'ottimizzatore per la
creazione del piano di esecuzione
Introduzione a PostgreSQL e ai database – Luca Ferrari
77 di 139 Vedere come viene trattata una query
2010­06­06 22:19:46 CEST STATEMENT: SELECT description FROM skill;
2010­06­06 22:19:46 CEST LOG: rewritten parse tree:
2010­06­06 22:19:46 CEST DETAIL: (
{QUERY :commandType 1 :intoClause <> :hasAggs false :hasWindowFuncs false :hasSubLinks false :hasDistinctOn false :hasRecursive false :cteList <> :rtable (
{RTE :alias <> :eref {ALIAS :aliasname skill :colnames ("skillpk" "skillid" "description" "skillfamilypk")
}
:rtekind 0 :relid 21220 :inh true :inFromCl true :requiredPerms 2 Introduzione a PostgreSQL e ai database – Luca Ferrari
78 di 139 Pagine dati
Introduzione a PostgreSQL e ai database – Luca Ferrari
79 di 139 Pagine dati
Le pagine dati rappresentano una
astrazione
rispetto
al
blocco
dati
memorizzato su disco
Una pagina dati ha sempre la stessa
struttura in memoria
All'interno di una pagina dati la singola
tupla è acceduta per mezzo di un offset,
ma indirettamente per consentire una
migliore gestione.
E' come se le foglie dell'indice fossere
contenuto nella pagina dati stessa!
Introduzione a PostgreSQL e ai database – Luca Ferrari
80 di 139 Struttura di una pagina dati
Page Header Data
pd_lsn
pd_tli
pd_lower
pd_upper
pd_special
pd_pagesize_version
pd_linp[ ]
linp0
linp1
linp2
linp3
linp4
linp5
...
linpN
La pagina contiene una serie di puntatori lineari (linear pointer – linp) che indirizzano le tuple contenute nella pagina stessa. L'offset della tupla non identifica la tupla stessa, ma il puntatore linp. In questo modo è possibile spostare la tupla (es. compattazione dello spazio) senza doverne cambiare l'offset.
pd_lower indica il punto “minimo” di inizio tuple (ossia dove finiscono i linp), tupleN
pd_upper il punto “massimo” di fine tupleN­1
.
tuple. Il range delle tuple è quindi .
identificato da pd_lower e pd_upper.
.
Le tuple sono organizzate a stack: ogni tuple0
nuovo tupla viene inserita in fondo Special space
subito prima delle altre (crescita verso l'alto) mentre il linp viene inserito all'inizio dopo gli altri (crescita verso 81 di 139 Introduzione a PostgreSQL e ai database – Luca Ferrari
pd_lsn: sincronizzazione con il WAL
Ogni pagina dati include un campo importante:
pd_lsn (Log Sequence Number).
Tale campo contiene il valore di segmento del log
WAL corrispondente all'ultima transazione che ha
“toccato” la pagina.
Questo numero serve a permettere la gestione
delle transazioni: la pagina dati può essere
scaricata su disco solo dopo che il log WAL è stato
scritto almeno fino al segmento indicato dal
pd_lsn.
Così facendo, il bgwriter è sicuro che in caso di
crash la transazione sia stata scritta nel WAL e la
pagina possa essere “ricreata”.
Introduzione a PostgreSQL e ai database – Luca Ferrari
82 di 139 Gestione dei buffer
Le pagine dati vengono caricate in memoria
condivisa in zone dette buffer
Ogni processo utente può accedere ai buffer
(e quindi alle pagine dati) rispettando alcune
regole per evitare corse critiche
Siccome portare una pagina da disco ad un
buffer richiede tempo, il sistema contiene un
flag IO_IN_PROGRESS per indicare che la
pagina/buffer sarà presto disponibile
Introduzione a PostgreSQL e ai database – Luca Ferrari
83 di 139 Locking dei buffer
Ogni buffer contiene due astrazioni utili per il locking, la loro
combinazione permette la gestione di piu' casi:
Buffer Content Lock: è un lock esplicito di tipo esclusivo, lavora a
livello di tupla
Page Pin: è un contatore del numero di processi che stanno
“agendo” sulla pagina dati, lavora a livello di pagina
I possibili casi sono:
Uno scan e un'analisi della visibilità di una tupla richiede un pin e un lock
Un Vacuum richiede un pin esclusivo (pin = 1) e un lock esclusivo sulla tupla da
cancellare. E' possibile che altri processi prendano il pin nel frattempo (pin > 1) ma
non che “vedano” la tupla o possano iniziare lo scan su di essa
E' possibile rilasciare il lock mantenendo il pin (tupla interessata da altro scan). La
tupla non può essere rimossa (pin > 1) e se la sua visibilità cambia ciò non
rappresenta un problema poiché la visibilità è legata allo scan
Cambiare i dati di visibilità XMIN/XMAX richiede un pin e un lock esclusivo, per evitare
che altri processi possano fare scan sulla tupla
Introduzione a PostgreSQL e ai database – Luca Ferrari
84 di 139 Ottenere un nuovo buffer
Ottenere un nuovo buffer risulta facile nel caso ci sia
spazio di memoria, mentre richiede di scartare delle
pagine qualora la memoria condivisa sia piena.
In una simile situazione si procede come segue:
Si sceglie un buffer fra quelli disponibili seguendo il puntatore
NextVictimBuf (algoritmo clock-skew).
Se il buffer ha un pin oppure è stato appena rimosso il suo pin
(contatore unpin > 0) allora non può essere scelto, si passa
oltre.
Se il buffer non ha pin e ha contatore di unpin pari a zero (pin
= unpin = 0) allora può essere scaricato: entra in gioco il
BackgroundWriter
Il BackgroundWriter acquisisce un pin e scrive le pagine su
disco
Introduzione a PostgreSQL e ai database – Luca Ferrari
85 di 139 Point In Time Recovery
Introduzione a PostgreSQL e ai database – Luca Ferrari
86 di 139 Point In Time Recovery (PITR)
PostgreSQL può sfruttare i log WAL per effettuare un backup del
sistema ad un determinato momento. L'idea è quella di archiviare
i log e di indicare al sistema che deve effettuare nuovamente le
transazioni archiviate.
0) impostazione del comando di archivio dei log (postgresql.conf):
archive_command = 'cp ­i %p /backup/wal//%f '
1) informare il database che si inizia il backup:
SELECT pg_start_backup('backup_pitr');
2) archiviazione della directory PGDATA (tar, ...)
3) informare il database che il backup è finito:
SELECT pg_stop_backup();
Introduzione a PostgreSQL e ai database – Luca Ferrari
87 di 139 PITR: ripristino
Una volta in possesso dei log è possibile ordinare al database di
fare un re-do delle transazioni:
1) stop del cluster
2) pulizia delle directory del cluster, in particolare pg_xlog
3) copia dei log dentro a pg_xlog
4) creazione di un file recovery.conf nella root
del cluster:
restore_command = ’cp /backup/wal/%f "%p"’
recovery_target_time=’2007­06­16 10:33:59’
5) avvio del cluster
Introduzione a PostgreSQL e ai database – Luca Ferrari
88 di 139 PITR: esempio
Si crea una grossa tabella,
si effettua il backup PITR
e si cancella la tabella.
Si ferma PostgreSQL, si cancellano
gli xlog dal backup ripristinato e
si crea il file recovery.conf (che sarà
poi eliminato automaticamente).
Introduzione a PostgreSQL e ai database – Luca Ferrari
89 di 139 PITR: esempio
Al riavvio il database
trova il file recovery.conf
e inizia a ripetere le transazioni
presenti nei WAL.
Il risultato è che si trovano
tutte le tabelle che erano state
cancellate!
Introduzione a PostgreSQL e ai database – Luca Ferrari
90 di 139 Indici
Introduzione a PostgreSQL e ai database – Luca Ferrari
91 di 139 Indici
PostgreSQL consente l'utilizzo di diversi tipi di indice, anche
lossy (che necessitano di un recheck). Il costo di accesso
tramite indice viene effettuato mediante la stima del
numero di tuple restituite e dell'operatore applicato,
assieme alle statistiche sulle colonne coinvolte
costo_indice = costo_seq * num_page_index // costo di acceso indice
+ (cpu_index_cost + index_tuple_cost) * num_index_tuple
// costo di analisi delle tuple indice
Introduzione a PostgreSQL e ai database – Luca Ferrari
92 di 139 Indici
B-tree: sono il default, vengono usati per query di uguaglianza e range
con operatori <, =, >. Su indice multi-colonna solo l'uguaglianza limita le
pagine di indice, gli altri operatori vengono rivalutati al volo. Viene usato nel
caso di pattern matching solo all'inizio del valore!
Bitmap: si costruisce una mappa di bit dove per ogni tupla si indica con
un bit se soddisfa (1) o non soddisfa (0) la condizione. Le mappe possono
poi essere unite con operatori logici per ridurre il set di risultati
velocemente.
Hash: utili solo per uguaglianze strette
GIST: Generalized Index Search Tree, fornisce una interfaccia per
l'integrazione di indici user-defined (es. indici R-Tree, per similarità, ecc.).
L'utente deve fornire l'implementazione di 7 funzioni C (consistent, union,
picksplit, compress, decompress, same, penalty)
GiN: indice inverso per la ricerca full-text (su colonne tsvector). E' più
veloce che un generico GiST e non è lossy. Utilizzano una coppia <chiave, posting_list> con i posting_list pointer che puntano ai documenti che
contengono la chiave. L'idea è che conviene fare la comparazione sulla
chiave che sul documento.
Introduzione a PostgreSQL e ai database – Luca Ferrari
93 di 139 Indici in PostgreSQL
PostgreSQL memorizza gli indici in modo disgiunto
dalla relazione alla quale puntano (indici
“secondari”)
Un indice viene memorizzato e gestito come una
normale relazione, e quindi è
Presente nel catalogo di sistema
Acceduto e gestito dal buffer manager
Ogni indice memorizza una coppia
<key, TIDs>
dove i Tuple Identifier sono coppie
<numero_blocco_dati, offset_linp>
per recuperare la pagina dati e la tupla all'interno
della pagina dati
Introduzione a PostgreSQL e ai database – Luca Ferrari
94 di 139 Indici e MVCC
A causa di MVCC di una tupla ne possono
esistere piu' versioni (teoricamente una valida
e le altre espirate o che stanno espirando)
L'indice memorizza la chiave comune a tutte le
versioni di una tupla e i TID di ogni versione di
una tupla
Di conseguenza l'indice sa come recuperare
ogni versione temporale di una tupla!
L'indice non contiene le informazioni di validità di
una tupla (XMIN, XMAX, CMIN, CMAX)!
Introduzione a PostgreSQL e ai database – Luca Ferrari
95 di 139 Indici e MVCC (2)
Perché un indice non contiene le informazioni di visibilità di
una tupla?
Una tupla potrebbe essere recuperata tramite scansione
sequenziale o un altro indice, e quindi le informazioni di visibilità
andrebbero replicate per ogni indice (spreco di spazio)!
Piu' indici possono essere combinati assieme per ridurre il
numero di tuple da recuperare, indipendentemente dalla loro
visibilità, e quindi è bene controllare la visibilità delle tuple
trovate solo alla fine!
La separazione tra indice e informazioni di visibilità può creare
alcuni disallineamenti fra indici e tuple:
INSERT → la tupla viene inserita prima nella pagina dati e poi
nell'indice. Ciò significa che non è immediatamente visibile, ma
ciò è coerente con il livello di isolamento READ_COMMITTED
VACUUM → la tupla viene cancellata prima dall'indice, in modo da
non essere piu' visibile ad altri processi (resta la scansione
sequenziale)
Introduzione a PostgreSQL e ai database – Luca Ferrari
96 di 139 Indici e MVCC (3)
La separazione fra indice e tuple dati puo' causare una corsa
critica nel passaggio (fly) dall'indice al dato (ad esempio se la
tupla viene modificata/rimossa da un altro processo)
Quando si accede ad una pagina indice si aggiunge un PIN alla
pagina dell'indice, così da notificare gli altri processi che le tuple
indicizzate non possono essere modificate (si ricordi che Vacuum
cancella prima la tupla dall'indice)
Introduzione a PostgreSQL e ai database – Luca Ferrari
97 di 139 Cool Features
cosa si può fare con PostgreSQL
Introduzione a PostgreSQL e ai database – Luca Ferrari
98 di 139 Utilities
pg_dump: effettua un dump di un database on-line,
producendo un file SQL (eventualmente compresso)
che contiene i comandi per la creazione del database,
dello schema e dei dati
pg_restore: ricostruisce un database, uno schema o i
dati partendo da un file di dump
vacuumdb: effettua il vacuum su un database
reindex: ricostruisce gli indici
copy, \copy: bulk loading di grosse moli di dati (il
default usato da pg_dump)
Initdb: inizializza una directory da usare per il cluster
(crea le cartelle pg_clog, pg_xlog, base, ...)
Introduzione a PostgreSQL e ai database – Luca Ferrari
99 di 139 Cool Features: replicazione
Slony: sistema masterslaves
Pgpool: replica le
modifiche a tutti i nodi, e
le interrogazioni a solo un
nodo
Pgcluster: analizza un
cluster e replica le
modifiche
Introduzione a PostgreSQL e ai database – Luca Ferrari
100 di 139 Cool Features: DBI­Link
Sviluppato principalmente da David Fetter, implementa
parzialmente
la
specifica
di
SQL:2003
SQL/MED
(Management of External Data).
Consente di accedere da un database PostgreSQL a data
che risiede in un altro database (anche di tipo diverso) in
modo trasparente.
Sostanzialmente si effettua una query verso una normale
tabella PostgreSQL, che in realtà è collegata ad una fonte
dati remota (es. database Oracle).
Si basa su Perl, e viene eseguito da PL/Perl. Introduzione a PostgreSQL e ai database – Luca Ferrari
101 di 139 Cool Features: PostGIS
PostGIS è un contrib che consente l'utilizzo di
sistemi GIS.
E' conforme alle specifiche OpenGIS "Simple
Features Specification for SQL" .
Esiste anche un client basato su Eclipse (uDig)
Introduzione a PostgreSQL e ai database – Luca Ferrari
102 di 139 Cool Features: Full Text Search
FTS utilizza i tsvector come tipi di dati sui quali
fare le ricerche. Le ricerche vengono specificate
tramite l'operatore @@.
Introduzione a PostgreSQL e ai database – Luca Ferrari
103 di 139 Cool Features: TOAST
TOAST (The Oversized-Attribute Storage Technique) è una tecnica per la
memorizzazione trasparente di dati che non possono essere contenuti in
una singola riga.
L'idea è di dividere il dato “largo” in pezzi (chunks) che vengono
memorizzati off-line in una tabella separata (TOAST-table). Questo viene
fatto ogni volta che la compressione del dato non permette la sua
memorizzazione in linea.
Il sistema ricollega all'esigenza i dati TOAST-ati.
TOAST può memorizzare i dati con quattro specifiche:
PLAIN: nessuna compressione o off-line, dati non TOAST-abili
EXTENDED: compressione e off-line (default)
EXTERNAL: no compressione, off-line
MAIN: compressione, no off-line (finché c'è spazio)
Introduzione a PostgreSQL e ai database – Luca Ferrari
104 di 139 Partitioning
(tramite ereditarietà e trigger)
Introduzione a PostgreSQL e ai database – Luca Ferrari
105 di 139 Partitioning
Il partitioning è una tecnica per la divisione fisica dei
dati pur mantenendoli logicamente uniti.
L'idea è quella di posizionare dati differenti su
supporti fisici (o posizioni) differenti, mantenendo
l'accesso ai dati trasparente alla suddivisione dei dati
stessi.
Ad esempio si immagini di avere una tabella che
riassume i movimenti di magazzino. Siccome tale
tabella può diventare molto grande nel tempo, è
opportuno separare fisicamente i dati in base, ad
esempio, alla data cui questi fanno riferimento.
Introduzione a PostgreSQL e ai database – Luca Ferrari
106 di 139 Partitioning: esempio di tabella
CREATE TABLE movmag (
CREATE TABLE movmag (
movmagpk serial NOT NULL,
movmagpk serial NOT NULL,
data date,
data date,
descrizione character varying DEFAULT 20,
descrizione character varying DEFAULT 20,
qta real )
qta real )
CREATE TABLE movmag2007 (
CREATE TABLE movmag2007 (
­­ Ereditato: movmagpk integer NOT NULL DEFAULT nextval('movmag_movmagpk_seq'::regclass),
­­ Ereditato: movmagpk integer NOT NULL DEFAULT nextval('movmag_movmagpk_seq'::regclass),
­­ Ereditato: data date,
­­ Ereditato: data date,
­­ Ereditato: descrizione character varying DEFAULT 20,
­­ Ereditato: descrizione character varying DEFAULT 20,
­­ Ereditato: qta real,
­­ Ereditato: qta real,
CONSTRAINT date_2007 CHECK CONSTRAINT date_2007 CHECK (data >= '2007­01­01'::date AND data <= '2007­12­31'::date)
(data >= '2007­01­01'::date AND data <= '2007­12­31'::date)
) INHERITS (movmag)
) INHERITS (movmag)
CREATE TABLE movmag2008 (
CREATE TABLE movmag2008 (
­­ Ereditato: movmagpk integer NOT NULL DEFAULT nextval('movmag_movmagpk_seq'::regclass),
­­ Ereditato: movmagpk integer NOT NULL DEFAULT nextval('movmag_movmagpk_seq'::regclass),
­­ Ereditato: data date,
­­ Ereditato: data date,
­­ Ereditato: descrizione character varying DEFAULT 20,
­­ Ereditato: descrizione character varying DEFAULT 20,
­­ Ereditato: qta real,
­­ Ereditato: qta real,
CONSTRAINT date_2008 CHECK CONSTRAINT date_2008 CHECK (data >= '2008­01­01'::date AND data <= '2008­12­31'::date)
(data >= '2008­01­01'::date AND data <= '2008­12­31'::date)
) INHERITS (movmag)
) INHERITS (movmag)
Introduzione a PostgreSQL e ai database – Luca Ferrari
107 di 139 Partitioning con un trigger
CREATE OR REPLACE FUNCTION _inserimento_movimento_magazzino()
RETURNS trigger AS
$BODY$
BEGIN
­­ controllo la data di inserimento IF NEW.data >= '01­01­2007'::date AND NEW.data <= '31­12­2007' THEN
RAISE INFO 'Inserimento in tabella movimento 2007';
INSERT INTO movmag2007(data, descrizione, qta) VALUES (NEW.data, NEW.descrizione, NEW.qta);
­­ non inserisco nulla nella tabella originale
RETURN NULL;
ELSIF NEW.data >= '01­01­2008'::date AND NEW.data <= '31­12­2008'::date THEN
RAISE INFO 'Inserimento in tabella movimento 2008';
INSERT INTO movmag2008(data, descrizione, qta)
VALUES (NEW.data, NEW.descrizione, NEW.qta);
­­ non inserisco nulla nella tabella originaria RETURN NULL;
ELSE
­­ inserimento nella tabella trash, errore! RAISE WARNING 'Inserimento nella tabella trash, controllare i vincoli!';
INSERT INTO movmag_trash(data, descrizione, qta)
VALUES (NEW.data, NEW.descrizione, NEW.qta);
­­ non inserisco nulla nella tabella originaria RETURN NULL;
END IF;
END;
$BODY$
LANGUAGE 'plpgsql' VOLATILE;
Introduzione a PostgreSQL e ai database – Luca Ferrari
108 di 139 Funzionamento del trigger
CREATE TRIGGER tr_inserimento
BEFORE INSERT ON movmag
FOR EACH ROW
EXECUTE PROCEDURE _inserimento_movimento_magazzino();
Introduzione a PostgreSQL e ai database – Luca Ferrari
109 di 139 Partitioning con una rule
CREATE OR REPLACE RULE inserimento_2007 AS
ON INSERT TO movmag
WHERE new.data >= '2007­01­01'::date AND new.data <= '2007­12­31'::date DO INSTEAD INSERT INTO movmag2007 (data, descrizione, qta) VALUES (new.data, new.descrizione, new.qta);
CREATE OR REPLACE RULE inserimento_2008 AS
ON INSERT TO movmag
WHERE new.data >= '2008­01­01'::date AND new.data <= '2008­12­31'::date DO INSTEAD INSERT INTO movmag2008 (data, descrizione, qta) VALUES (new.data, new.descrizione, new.qta);
In questo caso il trigger non scatta!
Introduzione a PostgreSQL e ai database – Luca Ferrari
110 di 139 Partitioning: accesso ai dati
Qualunque sia la scelta di partitioning effettuata, l'accesso ai
dati è trasparente.
Una volta interrogata la tabella movmag PostgreSQL va a
cercare fra i dati di tutte le figlie:
Vanno generati indici sulle varie tabelle per consentire un
accesso ai dati veloce!
Introduzione a PostgreSQL e ai database – Luca Ferrari
111 di 139 Explain:
capire come lavora l'ottimizzatore
Introduzione a PostgreSQL e ai database – Luca Ferrari
112 di 139 EXPLAIN
Explain è un comando SQL che consente di analizzare le
scelte dell'ottimizzatore per l'esecuzione di una query.
Explain si basa sulle statistiche raccolte durante il
funzionamento del database; le statistiche
possono essere aggiornate anche eseguendo
explain analyze.
Leggere l'output di explain è un'arte!
Introduzione a PostgreSQL e ai database – Luca Ferrari
113 di 139 Explain: primo esempio
Il piano di esecuzione indica:
Una scansione sequenziale sulla tabella (Seq Scan)
La dimensione delle tuple ritornate (152 byte)
Il numero di righe ritornate (440 !)
Il costo iniziale (0.00) e finale del piano (14.40)
C'è qualche cosa che non va!
Introduzione a PostgreSQL e ai database – Luca Ferrari
114 di 139 Explain: aggiornare le statistiche
La tabella talks è stata appena creata, e le sue statistiche non sono
aggiornate: l'ottimizzatore ipotizza che ci siano 440 righe al suo
interno! Rifacendo il piano di esecuzione con le statistiche aggiornate
si ha un valore migliore:
Introduzione a PostgreSQL e ai database – Luca Ferrari
115 di 139 Explain per grosse quantità dati Eliminando i vincoli di unicità è possibile duplicare rapidamente le
tuple all'interno di una tabella, arrivando fino a 6M tuple.
Introduzione a PostgreSQL e ai database – Luca Ferrari
116 di 139 Explain: perché non usa l'indice?
Introduzione a PostgreSQL e ai database – Luca Ferrari
117 di 139 Explain: l'ottimizzatore è furbo!
La query è una banale SELECT * e quindi si sta
chiedendo al database di restituire tutte le tuple
della tabella. Non ha senso quindi usare un indice,
perché tanto tutte le tuple devono essere
restituite senza ordinamento e senza condizioni.
La cosa cambia se si inseriscono clausole WHERE
o ORDER BY.
Introduzione a PostgreSQL e ai database – Luca Ferrari
118 di 139 Explain: utilizzo dell'indice
1) Si parte dal bitmap index scan (perché la condizione è di uguaglianza), quella che ha costo inziiale 0.00
2) Dalla scansione in indice, si estraggono circa 2M di tuple (vuote – senza dimensione – si prendono i puntatori)
3) Si ricontrolla la condizione su ogni tupla delle pagine dati e si estraggono le 2M tuple di lunghezza 32 byte
Introduzione a PostgreSQL e ai database – Luca Ferrari
119 di 139 Explain: ulteriore esempio
Si supponga di aver aggiunto una colonna durata (tipo real) e di averla
riempita con dati random.
1) Si parte dal bitmap index scan sulla clausola durata > 0
2) Si applica un filtro in memoria sulla clausola durata < 0.5
3) Si effettua un nuovo scan, questa volta delle tuple in memoria
4) Si effettua un ordinamento delle tuple
Introduzione a PostgreSQL e ai database – Luca Ferrari
120 di 139 Explain: ulteriore esempio
1) Si parte dal sequential scan (perché non esiste indice sfruttabile), quella che ha costo inziiale 0.00
2) Dalla scansione in indice, si estraggono circa 4M di tuple (complete di dati)
3) Si effettua un sort in memoria delle tuple restituite
Introduzione a PostgreSQL e ai database – Luca Ferrari
121 di 139 Window Functions
Introduzione a PostgreSQL e ai database – Luca Ferrari
122 di 139 Window Functions
Una window function è una funzione simile ad una
di aggregazione capace però di operare su una
partizione dei dati.
In sostanza per ogni tupla in uscita da una query
la window function è capace di operare una
aggregazione su un insieme ristretto di tuple
collegate a quella corrente.
Si utilizza la parola chiave OVER per indicare la
partizione sulla quale agire (che puo' essere
lasciata vuota ad indicare tutte le tuple).
Introduzione a PostgreSQL e ai database – Luca Ferrari
123 di 139 Window Functions: esempio
Si vuole contare, per ogni famiglia di competenze, quante
competenze ci sono:
SELECT sf.skillfamilyid, sf.description, count(s.skillid) OVER (PARTITION BY s.skillfamilypk) FROM skillfamily sf JOIN skill s ON s.skillfamilypk = sf.skillfamilypk
In
sostanza
viene
eseguito
un
“group-by”
su
s.skillfamilypk, e su questo si opera con la funzione di
aggregazione count().
Introduzione a PostgreSQL e ai database – Luca Ferrari
124 di 139 Common Table Expressions
Introduzione a PostgreSQL e ai database – Luca Ferrari
125 di 139 Commont Table Expressions
Le Common Table Expressions (CTE) consento la
scrittura di query complesse in modo piu' semplice.
Le CTE sono state introdotte con la versione 8.4 di
PostgreSQL, anche se erano già presenti in altri
motori relazionali.
I vantaggi nell'uso delle CTE sono:
Query complesse scritte in modo piu' semplice e
sintetico (simile per questo alle viste)
Maggiore velocità di esecuzione (l'ottimizzatore
comprende meglio la query)
Introduzione a PostgreSQL e ai database – Luca Ferrari
126 di 139 CTE: concetti generali e sintassi
Una query che usa la CTE può essere
divisa in due parti principali:
La query principale
Una query secondaria
WITH temporary_table_name AS ( <query secondaria> )
WITH temporary_table_name AS ( <query secondaria> )
<query principale>
<query principale>
Introduzione a PostgreSQL e ai database – Luca Ferrari
127 di 139 CTE: considerazioni
Il sistema genera una tabella temporanea con i risultati della
1
query secondaria
La query principale puo' fare riferimento alla tabella
temporanea 2
Quando la query principale termina la tabella temporanea
3
viene distrutta automaticamente
WITH random_number_table AS WITH random_number_table AS 1
( SELECT random() AS number )
( SELECT random() AS number )
SELECT 'now'::text::timestamp AS timestamp, SELECT 'now'::text::timestamp AS timestamp, random_number_table.number;
random_number_table.number;
2
3
Introduzione a PostgreSQL e ai database – Luca Ferrari
128 di 139 CTE: un esempio
Date le tabelle skill e skillfamily, collegate dalla chiave esterna skillfamilypk, la seguente query estrae tutte le skill che appartengono ad una famiglia con piu' di 3 skill:
select s.skillid, s.description from skill s where s.skillfamilypk in (select ss.skillfamilypk from skill ss group by ss.skillfamilypk having count(ss.skillid) > 3);
skill
skillfamily
skillfamilypk
Mediante CTE la query può essere riscritta come:
with family_3 as (select skillfamilypk from skill group by skillfamilypk having count(skillpk) > 3)
select s.skillid, s.description from skill s JOIN family_3 ON s.skillfamilypk = family_3.skillfamilypk;
Introduzione a PostgreSQL e ai database – Luca Ferrari
129 di 139 Senza CTE cosa succede?
explain select s.skillid, s.description from skill s where s.skillfamilypk in (select ss.skillfamilypk from skill ss group by ss.skillfamilypk having count(ss.skillid) > 3);
QUERY PLAN ­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­
Hash Semi Join (cost=7.55..16.40 rows=246 width=50)
Hash Cond: (s.skillfamilypk = ss.skillfamilypk)
­> Seq Scan on skill s (cost=0.00..5.46 rows=246 width=54)
­> Hash (cost=7.27..7.27 rows=23 width=4)
­> HashAggregate (cost=6.69..7.04 rows=23 width=13)
Filter: (count(ss.skillid) > 3)
­> Seq Scan on skill ss (cost=0.00..5.46 rows=246 width=13)
(7 rows)
Introduzione a PostgreSQL e ai database – Luca Ferrari
130 di 139 Con CTE cosa succede?
explain with family_3 as (select skillfamilypk from skill group by skillfamilypk having count(skillpk) > 3)select s.skillid, s.description from skill s JOIN family_3 ON s.skillfamilypk = family_3.skillfamilypk;
QUERY PLAN ­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­
Hash Join (cost=7.78..16.63 rows=246 width=50)
Hash Cond: (s.skillfamilypk = family_3.skillfamilypk)
CTE family_3
­> HashAggregate (cost=6.69..7.04 rows=23 width=8)
Filter: (count(skill.skillpk) > 3)
­> Seq Scan on skill (cost=0.00..5.46 rows=246 width=8)
­> Seq Scan on skill s (cost=0.00..5.46 rows=246 width=54)
­> Hash (cost=0.46..0.46 rows=23 width=4)
­> CTE Scan on family_3 (cost=0.00..0.46 rows=23 width=4)
(9 rows)
Introduzione a PostgreSQL e ai database – Luca Ferrari
131 di 139 Senza CTE: un altro esempio
explain select c.* from cronologia c where c.visibile='t' and c.id_cronologia = (select max(cc.id_cronologia) from cronologia cc where c.id_elemento = cc.id_elemento);
QUERY PLAN ­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­
Seq Scan on cronologia c (cost=0.00..526745.55 rows=149 width=39)
Filter: (visibile AND (id_cronologia = (SubPlan 1)))
SubPlan 1
­> Aggregate (cost=14.59..14.60 rows=1 width=4)
­> Index Scan using idx_id_elemento_data_creazione on cronologia cc (cost=0.00..14.58 rows=3 width=4)
Index Cond: ($0 = id_elemento)
(6 rows)
Introduzione a PostgreSQL e ai database – Luca Ferrari
132 di 139 CTE: un altro esempio
explain with max_cronologia as (select c.* from cronologia c where c.visibile='t' and c.id_cronologia = (select max(id_cronologia) from cronologia where id_elemento = c.id_elemento))
select * from max_cronologia;
QUERY PLAN ­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­
CTE Scan on max_cronologia (cost=526745.55..526748.53 rows=149 width=65)
CTE max_cronologia
­> Seq Scan on cronologia c (cost=0.00..526745.55 rows=149 width=39)
Filter: (visibile AND (id_cronologia = (SubPlan 1)))
SubPlan 1
­> Aggregate (cost=14.59..14.60 rows=1 width=4)
­> Index Scan using idx_id_elemento_data_creazione on cronologia (cost=0.00..14.58 rows=3 width=4)
Index Cond: (id_elemento = $0)
(8 rows)
Il costo finale è lo stesso!
Non sempre le CTE danno prestazioni migliori!
Introduzione a PostgreSQL e ai database – Luca Ferrari
133 di 139 Con CTE: un altro esempio
explain with visibile_cronologia as ( select c.id_cronologia, c.id_elemento from cronologia c where c.visibile='t' ), max_cronologia as (select c.* from cronologia c where c.id_cronologia = (select max(v.id_cronologia) from visibile_cronologia v where v.id_elemento = c.id_elemento) )
select * from max_cronologia;
QUERY PLAN ­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­
CTE Scan on max_cronologia (cost=24144719.85..24144723.45 rows=180 width=65)
CTE visibile_cronologia
­> Seq Scan on cronologia c (cost=0.00..672.15 rows=29777 width=8)
Filter: visibile
CTE max_cronologia
­> Seq Scan on cronologia c (cost=0.00..24144047.70 rows=180 width=39)
Filter: (id_cronologia = (SubPlan 2))
SubPlan 2
­> Aggregate (cost=670.36..670.37 rows=1 width=4)
­> CTE Scan on visibile_cronologia v (cost=0.00..669.98 rows=149 width=4)
Filter: (id_elemento = $1)
In questo caso il costo è addirittura peggiore!
Introduzione a PostgreSQL e ai database – Luca Ferrari
134 di 139 CTE ricorsive
Le CTE prevedono l'uso ricorsivo:
Deve essere valutato prima un termine non
ricorsivo
Si congiunge il termine non ricorsivo con quello
ricorsivo (ad esempio UNION ALL)
Si scrive il termine ricorsivo che fa riferimento
alla tabella temporanea stessa
In sostanza la tabella temporanea può fare
riferimento a se stessa!
C'è il rischio di loop infiniti!
Introduzione a PostgreSQL e ai database – Luca Ferrari
135 di 139 CTE ricorsive: esempio
Termine non ricorsivo
WITH RECURSIVE counter_table AS ( SELECT 1 AS initial_counter UNION ALL Congiunzione
SELECT ct.initial_counter + 1 Termine ricorsivo: FROM counter_table ct fa riferimento alla WHERE ct.initial_counter < 100 )
tabella temporanea!
SELECT * FROM counter_table;
Termine per evitare loop infiniti!
CTE Scan on counter_table (cost=2.95..3.57 rows=31 width=4)
CTE counter_table
­> Recursive Union (cost=0.00..2.95 rows=31 width=4)
­> Result (cost=0.00..0.01 rows=1 width=0)
­> WorkTable Scan on counter_table ct (cost=0.00..0.23 rows=3 width=4)
Filter: (ct.initial_counter < 100)
Introduzione a PostgreSQL e ai database – Luca Ferrari
136 di 139 CTE: come funziona la ricorsione?
Si valuta la query temporanea
Il sistema valuta il termine non ricorsivo (ossia
quello che può essere evaluato alla prima
“passata”)
Si costruisce una working table vuota
Si effettua l'unione del termine non ricorsivo
inserendo il risultato di ogni interazione nella
working table
Quando la ricorsione finisce si valuta la
query principale.
Introduzione a PostgreSQL e ai database – Luca Ferrari
137 di 139 CTE ricorsive: ulteriore esempio
Data la seguente tabella gerarchica
possiamo estrarre i dati dei “fratelli”:
select * from hierarchy ;
pk | parentpk ­­­­+­­­­­­­­­­
1 | 2 | 3 | 1
4 | 2
5 | 1
6 | 2
(6 rows)
WITH RECURSIVE hierarchy_exploded AS (
WITH RECURSIVE hierarchy_exploded AS (
SELECT pk, parentpk, ARRAY[pk] AS brothers SELECT pk, parentpk, ARRAY[pk] AS brothers FROM hierarchy WHERE parentpk IS NULL
FROM hierarchy WHERE parentpk IS NULL
UNION ALL
UNION ALL
SELECT h.pk, h.parentpk, he.brothers || SELECT h.pk, h.parentpk, he.brothers || ARRAY[h2.pk]
ARRAY[h2.pk]
FROM hierarchy h JOIN hierarchy_exploded FROM hierarchy h JOIN hierarchy_exploded he ON h.parentpk = he.pk JOIN hierarchy h2 he ON h.parentpk = he.pk JOIN hierarchy h2 ON h2.parentpk = h.parentpk )
ON h2.parentpk = h.parentpk )
SELECT * FROM hierarchy_exploded;
SELECT * FROM hierarchy_exploded;
Introduzione a PostgreSQL e ai database – Luca Ferrari
138 di 139 Conclusioni
PostgreSQL è un database maturo, scalabile, affidabile
e soprattutto di classe enterprise.
Il suo utilizzo è raccomandato quando l'integrità dei dati
è molto importante, o meglio quando perdere una
singola tupla può compromettere tutto il database.
E' un ottimo database di ricerca, grazie alla possibilità
di inserire oggetti user-defined al suo interno.
E' un'ottima piattaforma per prendere confidenza con
database enterprise.
Introduzione a PostgreSQL e ai database – Luca Ferrari
139 di 139