Nella parte iniziale del testo troviamo un'ampia sezione di introduzione, nella quale spiccano i
foreword di due riconosciuti esperti del settore performance di
Oracle -
Cary Millsap autore di
Optimizing Oracle Performance e
Jonathan Lewis, autore di
Cost-Based Oracle: Fundamentals - che sottolineano l'ottimo lavoro fatto dall'autore nel creare una base di conoscenza e una proposta di approccio, uniti a un taglio ragionevolmente pratico.
Segue una breve presentazione dell'autore
Christian Antognini e dei
technical reviewer Alberto Dell'Era,
Francesco Renne,
Joze Senegacnik e
Urs Meier. Dopo i classici ringraziamenti troviamo un'introduzione alla struttura del libro e un accenno ai destinatari di questo, che sono analisti di performance, sviluppatori di applicazioni e amministratori di database.
Tutti gli innumerevoli esempi di codice presentati possono essere scaricati dal sito del libro. L'introduzione si chiude con la presentazione dell'
OakTable Network, una comunità che vuole adottare un approccio scientifico allo studio delle tecnologie concernenti il mondo
Oracle; nata da incontri di un ristretto gruppo di persone, in casa dell'autore, questa comunità vanta oggi un sito che raccoglie materiale sull'argomento.
La
prima parte del libro intitolata
Foundation si apre con una citazione del nostro
Niccolò Macchiavelli, tratta dalla sua opera più importante
Il Principe, e vuole introdurre alcuni concetti di base relativi ai problemi di performance. Questa parte si compone di due capitoli.
Nel
primo capitolo, viene effettuata un'introduzione di base sui
Performance problem, rilevando la necessità di adottare un approccio metodologico all'argomento performance sin dall'inizio di un progetto: fare un'analisi dei requisiti di performance richiesti, ma anche pensare a questi in fase di design, mentre si scrive codice e unità di testing. Un breve accenno viene fatto agli scenari che porteranno all'evidenza di problemi di performance. E si conclude il capitolo proponendo un approccio ai problemi di performance tramite due roadmap: tre fasi per la catalogazione dei problemi relativi alle performance e altre tre per lavorare alla risoluzione delle problematiche. I 3 passi per analizzare e risolvere i problemi sono: l'individuazione del punto in cui viene consumato il tempo; in che modo viene consumato e infine come può essere ridotto questo tempo.
Il
secondo capitolo invece introduce dei concetti chiave, che saranno utilizzati e approfonditi nel resto del libro. Si inizia con la definizione dei concetti base di
selettività, un numero che delinea la scrematura di dati effettuati con una particolare query e di
cardinalità, il numero di righe ritornate da una determinata query. Viene poi analizzato il
ciclo di vita di un cursore, la cui conoscenza permette di focalizzare meglio i punti in cui intervenire per l'ottimizzazione. Si entra ancora più in dettaglio, esaminando la fase del ciclo che consiste nel parsing del codice SQL. Segue la presentazione dei cursori
Shareable e la spiegazione delle casistiche in cui questi vengono utilizzati automaticamente in
Oracle. A seguire un'approfondita trattazione sulle variabili
Bind, con una disamina dei pregi e difetti di questa feature e degli scenari in cui è vantaggioso o meno utilizzarle. Un ultimo paragrafo introduce alla modalità di lettura e scrittura di blocchi dei
datafile.
La
seconda parte, denominata
Identification, è composta dal solo
terzo capitolo, come a indicare la delicatezza e l'importanza della fase iniziale di
Identifying Performance Problems. Lo scopo di questo capitolo è la presentazione delle tecniche di indagine per rispondere alle prime due domande del flusso proposto nel primo capitolo: dove e come viene speso il tempo nell'elemento che presenta problemi di performance? Verranno descritte diverse metodologie di identificazione applicabili ai diversi strati di software. Come strategia primaria viene consigliato di dividere la piattaforma che si sta analizzando in parti software diverse, per poter procedere all'analisi dei singoli moduli. Viene poi presentata un
analysis roadmap, un diagramma di flusso che guida proceduralmente alla identificazione dello strato di software che funge da collo di bottiglia. Per poter effettuare queste accurate analisi vengono proposti i due metodi principali: l'
instumentation e il
profiling. Nel paragrafo dedicato all'
instumentation vengono proposti esempi di come utilizzare questa tecnica nel codice applicativo,
Java in particolare, e nelle chiamate di database,
PL/SQL,
OCI,
JDBC e
ODP.NET. Il paragrafo sul
profiling risulta molto dettagliato nella presentazione di un tool commerciale,
PerformaSure, che viene utilizzato in tutte le varie fasi di registrazione e reporting delle informazioni per spiegare bene i concetti; fino ad arrivare al
profiling di dettaglio che esamina le singole linee di codice. Gli esempi sono presentati con
Java e
JProbe. Si passa poi ad esaminare con estrema accuratezza il
tracing delle chiamate di database, attivabile da
Oracle. Vengono indicati i metodi di attivazione e i parametri e poi si passa a esaminare la struttura di un file di
trace e come estrarne informazioni. In particolare viene presentato il tool
TRCSESS disponibile in
Oracle 10g, che è un'elementare utility di estrazione informazioni e
TKPROF sempre ditribuito con il DBMS, che viene utilizzato per analizzare le informazioni estratte. Per quest'ultimo vengono descritti tutti i parametri in dettaglio e poi l'autore presenta il tool di analisi
TVD$XTAT che ha creato lui stesso. Dopo questa approfondita disamina dei possibili output si termina esaminando il
profiling del codice
PL/SQL.
Con la
terza parte,
Query Optimizer, si intende presentare non tanto il funzionamento dettagliato dell'
ottimizzatore di Oracle, quanto piuttosto le varie funzionalità basilari che occorre conoscere per interagire proficuamente con questo elemento essenziale del motore di database. Ecco quindi che nel
quarto capitolo, si esaminato le
System and Object Statistics, nucleo di informazioni di base che vengono utilizzate dall'
ottimizzatore di query per costruire un
execution plan, che sia oltre che funzionalmente corretto anche efficiente. Dopo un primo accenno al package
dbms_stats,
vengono esaminate nel dettaglio le statistiche di sistema, introdotte per la prima volta in
Oracle 9i; a supporto del nuovo metodo di ottimizzazione il
CPU cost model. Si entra approfonditamente nel dettaglio esaminando i
due tipi di statistiche di sistema: quelle senza workload, disponibili di default in
Oracle 10g e quelle con workload, che devono essere computate a comando e che intendono dare una vista più reale, in un ambiente in evoluzione, dei parametri di sistema considerati nelle statistiche. Viene poi illustrato l'impatto delle statistiche di sistema sull'
ottimizzatore di query. Si passa poi ad esaminare in maniera davvero approfondita, le
Object Statistics, statistiche che intendono riassumere la configurazione/assetto dei vari oggetti di database, tabelle, indici, colonne, per dare all'
ottimizzatore di query una via efficiente di costruzione del piano di esecuzione. Vengono esaminate le statistiche disponibili, le viste e tabelle di
data dictionary dove queste vengono memorizzate, e il loro significato, partendo dalle statistiche relative alle tabelle. Viene poi esaminato il concetto di istogramma come rappresentazione della distribuzione di dati in un oggetto di database e la relativa memorizzazione in
Oracle di queste statistiche. Un accenno alle
extended statistics e si passa poi alla disamina delle statistiche relative agli indici. Dopo la panoramica sulle statistiche disponibili,
si procede esaminando il package dbms_stats e le sue procedure gather_, le quali permettono di calcolare le statistiche a diversi livelli di granularità, per i diversi oggetti e con una serie ampia di parametrizzazioni. La trattazione è veramente di dettaglio e si va ad analizzare parametro per parametro tutte le query per calcolare recuperando le statistiche. Si parla poi della configurazione del
dbms_stats e della schedulazione del recupero delle statistiche, sia per
Oracle 10g che per
Oracle 11g. Vengono poi passate in rassegna tutta una serie di possibilità per la gestione delle statistiche come le tabelle di backup, le statistiche
pending, e il
locking di queste, così come la possibilità di effettuare comparazioni e cancellazioni di statistiche. Un paragrafo descrive le possibili strategie di aggiornamento delle statistiche e la parte finale è dedicata ai servizi di contorno disponibili per esse come il mantenimento dello storico, l'export e l'import delle statistiche, il
logging della produzione di queste.
Il
quinto capitolo,
Configuring the Query Optimizer si focalizza sul settaggio dei parametri di configurazione dell'
ottimizzatore di query. Dopo la consueta presentazione di una roadmap, una procedura da seguire per effettuare la registrazione di tutti questi parametri, si iniziano ad esaminare in dettaglio uno per uno. Oltre ai possibili valori, viene per ognuno, fornita una spiegazione dell'influenza che hanno sull'
ottimizzatore; si ha quindi la possibilità di spaziare dalla struttura dei segmenti di dati, alle operazioni effettuate in un accesso a una tabella tramite
index range scan. L'ultimo paragrafo si occupa dei parametri concernenti la
PGA. Anche in questo caso l'autore non si limita a presentare dei parametri, ma si sofferma a spiegarne i risvolti, con esempi e grafici che sono estremamente chiari.
Il
sesto capitolo è una piccola trattazione sui piani di esecuzione, operazioni effettuate dall'engine SQL per elaborare una query: infatti quando nonostante sia stata adottata la migliore configurazione l'
ottimizzatore una query risulti non efficiente, sarà questo il primo elemento da analizzare. La prima parte illustra i quattro diversi metodi per ottenere un piano di esecuzione, dal comando
EXPLAIN PLAN, alla consultazione delle viste dinamiche di performance, dall'utilizzo dell'
AWR, all'utilizzo di alcune funzionalità di
tracing non documentate. Viene esaminato approfonditamente il package
DBMS_XPLAN, le sue funzioni con i relativi parametri e le colonne della tabella, che conterrà il piano di esecuzione.
La seconda parte si sofferma invece sull'interpretazione del piano ottenuto, dal punto di vista della sua struttura: vengono illustrate l'alberatura padre-figlio presente nel piano di esecuzione e le varie operazioni che si possono trovare in esso, dai
NESTES LOOPS, ai
FILTER, UPDATE fino al
CONNECT BY delle query gerarchiche; viene spiegato come ricondurre piani di esecuzione complessi ai suoi elementi principali e come riconoscere piani di esecuzione inefficienti con due verifiche di correttezza del lavoro svolto dall'
ottimizzatore.
Il
settimo capitolo si sofferma sulle tecniche di
tuning SQL disponibili in
Oracle, quando usarle e quali sono gli eventuali effetti positivi e negativi. Viene quindi esaminata la possibilità di alterare le strutture di accesso ai dati, indici, viste materializzate, partizioni; di intervenire sulla query stessa; di utilizzare gli
hint in
Oracle, con un buon livello di approfondimento; infine si passa all'intervento sull'ambiente di esecuzione. Segue una ampia panoramica sull'
Automatic Tuning Optimizer, una novità introdotta con
Oracle 10g che consente di delegare all'
ottimizzatore, il
tuning delle query. La panoramica è veramente accurata, con schemi di funzionamento, innumerevoli esempi di utilizzo del package
DBMS_SQLTUNE e illustrazione delle operazioni di gestione, alterazione, migrazione, cancellazione dei dati di profiling raccolti. Si prosegue con una presentazione delle
Stored Outlines, oggetti di database associati alle query per rendere stabili i piani di esecuzione, seguite dalle
SQL Plan Baseline, che in
Oracle 11g rappresentato un miglioramento delle prime.
Con la
quarta parte si entra nel dettaglio dell'accesso efficiente ai dati e dell'ottimizzazione delle
join, e di come utilizzare al meglio le innumerevoli funzionalità atte a migliorare le performance del codice SQL. L'
ottavo capitolo,
Parsing, si focalizza sulle problematiche relative a questa fondamentale attività. Vi è una prima parte tesa a dare le basi per la corretta individuazione dei problemi di
parsing, utilizzando i profiler per l'analisi dei file di
trace. Successivamente si passa a esaminare le possibilità per provare a risolvere dette problematiche: partendo dall'ottimizzazione per passi di
quick parse ripetuti numerose volte, utilizzando le variabili
bind, i
prepared statement e la cache dei comandi lato client, e arrivando velocemente ai
long parse e alla metodologia delle
Stored Outlines per ridurne i tempi. Per migliorare le performance di un'applicazione, nell'impossibilità di modificarne il codice, viene mostrato l'utilizzo delle impostazioni di
cursor sharing e la
caching degli statement lato server.
Tutte queste parti sono accompagnate da grafici, che rendono immediatamente idea del guadagno ottenuto tramite i vari interventi descritti. L'ultima parte del capitolo esamina l'utilizzo delle tecniche descritte tramite le varie
API; dopo una tabella che riassume quale tecniche si hanno a disposizione con i diversi linguaggi, inizia una serie di esempi, con tanto di codice utilizzando
PL/SQL, sia statico che dinamico; con le
OCI, tramite
JDBC e con
ODP.NET.
L'ottimizzazione dell'accesso ai dati è l'argomento del
nono capitolo. Nella prima parte viene illustrato come identificare accessi ai dati inefficienti, tramite le statistiche sulle letture logiche; viene richiamato il concetto di selettività e la sua importanza nell'accesso alle strutture dati del DB, con una serie di test e relative distribuzioni delle letture logiche in rapporto ad essa e alle varie strutture di memorizzazione dei dati. Si entra poi nel dettaglio delle tipologie di accesso, che vengono fatte con presenza di indici, partizioni,
hash cluster e per query con alta selettività. Ne esce un'analisi approfondita del partizionamento e delle modalità di selezione delle partizioni da parte dell'
ottimizzatore in base alle clausule
WHERE. Terminata questa panoramica, vengono effettuate alcune considerazioni di design delle partizioni. Per le query con bassa selettività, invece, viene effettuato un approfondimento sugli indici, con il loro fattore di
clustering, e sia per guelli
B-tree che per quelli
Bitmap, vengono analizzate le varie condizioni di filtro: eguaglianza,
null,
range,
like. Segue una breve panoramica su alcune tipologie particolari di indici: quelli basati su una funzione, quelli composti, e i
linguistic index. Il capitolo termina con un paragrafo sulle singole tabelle
hash cluster, da utilizzare, con ottime performance, per memorizzare tabelle di
lookup.
In
Optimizing Joins, il
decimo capitolo del libro, viene presentata una trattazione adeguatamente completa relativa al miglioramento delle performance nelle operazioni di
join. La prima parte si sofferma sui termini e i concetti, per dare definizioni chiare e precise sui
join tree; sulle diverse tipologie di
join,
cross join,
equi join,
outer join e altre, e sulla differenza tra le condizioni di
join e i filtri sulle tabelle. Dopo la parte introduttiva ci si addentra nella gestione dei
join da parte dell'
ottimizzatore; vengono quindi discussi i piani di esecuzione con i metodi usati dal database engine:
Nested Loop Join,
Outer Join,
Merge Join e
Hash Join. Per questi ultimi ci si addentra anche nella spiegazione sull'utilizzo di aree di memoria o disco per la memorizzazione delle informazioni temporanee. Segue l'introduzione della parallelizzazione nelle operazioni di
join, sia per tabelle partizionate che non. Infine vengono discusse le trasformazioni effettuate sulle query, dal motore, per ottimizzarne le prestazioni.
Dopo le ottimizzazioni definite nei precedenti capitoli che devono essere considerate propedeutiche, nell'
undicesimo capitolo si considerano tecniche avanzate di ottimizzazione.
Per ognuna delle tecniche presentate vi è un parte introduttiva, una spiegazione di come funziona la tecnica, quando usarla ed eventuali controindicazioni. Si inizia con le
viste materializzate di cui si introduce la sintassi, la funzionalità di
query rewriting e le modalità di
refreshing; si prosegue con il
Result Caching per la memorizzazione in un buffer dei dati lato server, da
PL/SQL o da client; è poi la volta di una corposa discussione sul
Parallel Processing, il funzionamento, la corretta configurazione, quali query, DML e DDL possono essere parallelizzate. Vi è anche un'ampia sezione di avvertimento sull'utilizzo corretto di questa funzionalità. La successiva funzionalità esaminata è il
Direct-Path Insert per poi chiudere con il
Row Prefetching e l'
Array Interface, funzionalità per le quali vengono mostrati esempi nei diversi linguaggi
PL/SQL,
OCI,
JDBC e
ODP.NET.
Il testo si conclude con il
dodicesimo capitolo, che si focalizza sull'ottimizzazione della progettazione fisica del database. Il capitolo si apre evidenziando l'importanza dell'ordine delle colonne nella definizione delle tabelle a causa della modalità di memorizzazione fisica dei record nei blocchi. Nel paragrafo successivo vengono evidenziate tutta una serie di problematiche, molto comuni, derivanti da un'errata definizione del
datatype per le colonne; di conseguenza segue un paragrafo dedicato alle
Best Practices nella scelta dei
datatype. Si affronta poi il problema delle
Row Migration e
Row Chaining che riguarda la memorizzazione delle righe nei blocchi, e che incide sulle performance se non risolto. Gli ultimi due paragrafi si focalizzano sul
Block Contention, l'accesso di diversi processi allo stesso blocco, e la compressione di dati, che in una certa ottica può essere utilizzata per ottimizzare la dimensione dei dati trattati.
La
quinta parte del libro è costituita dalle appendici: l'
Appendice A sono i file scaricabili con tutto il codice, prettamente SQL, presentato nel libro. Ed è una ricca collezione di utili query a supporto delle attività di analisi delle performance ed ottimizzazione delle prestazioni. Mentre l'
Appendice B è la bibliografia.
Il testo in esame credo possa rappresentare un MUST per chi intende cominciare un percorso di approfondimento relativamente all'ottimizzazione delle performance in
Oracle; tra l'altro è un libro che può essere di utilità per diverse figure, come del resto era nelle intenzioni dell'autore, così può giovare tanto agli Amministratori di Database, quanto agli Sviluppatori.