30 agosto 2009

Particolarità del mirror ASM

Recentemente ho voluto utilizzare ASM su un vecchio server single-instance con 8 dischi SCSI di vecchio tipo da 18 GB; tutto sommato il server se la cava, avendo 4 processori e 8 GB di RAM.
Visto che il server non è di front-end, e nonostante fosse equipaggiato con un controller SCSI RAID, ho voluto ugualmente affidare ad ASM la gestione del mirror, confidando nella maggiore efficienza di ASM nel massimizzare le prestazioni con uno storage multidisco.

In seguito alla configurazione in normal redundancy, che consiste nella memorizzazione di due copie di ogni extent su due dischi diversi, rileggendo meglio il manuale mi sono accorto di un paio di piacevoli "effetti collaterali" del mirroring "intelligente" di ASM.
Consideriamo ad esempio il caso di 8 dischi: se si rompe un disco ASM automaticamente copia tutti gli extent persi sugli altri dischi disponibili, partendo dalle copie degli extent presenti, ottenendo un array di 7 dischi che... è ancora in normal redundancy! E la redundancy viene mantenuta fino a che c'è spazio disponibile sui dischi rimanenti per ospitare tutti gli extent necessari al database; virtualmente si potrebbe eliminare un disco alla volta, attendendo la risincronizzazione, fino ad avere solo due dischi e ancora mirroring; o anche un solo disco, senza più redundancy, ma con tutti i dati sempre disponibili.
Come corollari deriviamo che, per prima cosa, è possibile costruire un array con mirroring semplice con un numero qualsiasi di dischi (maggiore di 1); secondo, perde significato il disco di "hot-spare", sostituito dallo spazio libero residuo su ogni disco.
Decisamente molto più efficiente rispetto ai sistemi di storage normali.

Casualmente, poi, i dischi che compongono l'array sono di capacità identica ma di diverse prestazioni. Ciò si riflette anche nelle misure di prestazioni fatte internamente da ASM.
Nell'immagine seguente si può notare come ASM riesca a massimizzare le prestazioni utilizzando maggiormente i dischi più veloci, sia a causa della latenza che del throughput.

26 agosto 2009

Estate, mare e Oracle

La parte più "vivace" dell'estate è ormai alle spalle, ma concediamoci qualche altro momento di svago con un video HD di BMW Oracle Racing Technology. Altrimenti si diverte solo Larry con questi "giocattoli".
Chissà se tutti quei dati raccolti, di cui si parla nel video, vengono immagazzinati in un database Oracle, e chissà dove si trova :-)

A presto per i nuovi post!

24 giugno 2009

Le novità di ASM in 11g

La versione 11g di ASM include alcuni miglioramenti interessanti.
  • Comando CP: finalmente è possibile copiare i file da e verso lo storage ASM, come del resto qualsiasi processo server di Oracle può fare da sempre. La differenza è che si usa il comando CP da linea di comando ASDCMD o da linea di comando della shell come parametro di ASMCMD.

  • Fast Mirror Resync: in seguito a temporanee indisponibilità di un disco ASM contenente copie di dati mirrorati, la risincronizzazione avviene tenendo conto dei dati già presenti sul disco, evitando quindi la ricostruzione completa del mirror. ASM in pratica tiene traccia degli extent da aggiornare e al resync copia solamente quelli. È stato introdotto il parametro DISK_REPAIR_TIME, che definisce un periodo trascorso il quale il disco non disponibile viene ricostruito per intero.

  • Extent di dimensioni variabili: con 11g è possibile definire la dimensione degli extent, ovvero le unità di allocazione con cui viene riservato lo spazio per i file nello storage ASM. Nelle versioni precedenti questa dimensione era fissa e pari a 1 MB per i file "normali" e 128 KB per i file ad alto I/O rate. I 128 KB rimangono anche in 11g. Una conseguenza immediata è che aumenta ancora la quantità totale di dati che si possono immagazzinare nel database, potendo disporre di datafile più grandi.

07 giugno 2009

Hugepages in RHEL5

Abbiamo visto, in un post precedente, come sia possibile per Oracle allocare una buffer cache molto grande, fino a 62 GB, su un sistema Linux a 32 bit.

La buffer cache viene sostanzialmente allocata come un file su un filesystem di tipo ramfs, e l'accesso ai blocchi in memoria avviene in maniera indiretta.

Facciamo un passo indietro. La memoria nei sistemi a memoria virtuale (ad esempio Linux) viene allocata tramite pagine di 4 KB mappate sulla memoria fisica da una tabella di lookup locale che referenzia una tabella di lookup di sistema, la quale punta finalmente alla memoria fisica.

Le hugepage sono pagine di dimensione molto maggiore di quella standard. Nel caso di RHEL5 ho verificato una dimensione di 2 MB (almeno 500 volte la dimensione standard):
$ grep Huge /proc/meminfo 
HugePages_Total: 821
HugePages_Free: 558
HugePages_Rsvd: 555
Hugepagesize: 2048 kB

Le hugepage vengono allocate al boot tramite un parametro del kernel, in modo da riservare un numero di porzioni contigue di RAM sifficienti ad allocare la quantità di hugepage richieste, altrimenti la RAM viene allocata in pagine da 4 KB (il default). Inoltre le hugepage non sono swappabili; in linguaggio Oracle si potrebbero considerare pinned.
In generale quindi, per allocare quantitativi di RAM molto grandi e mantenere le tabelle di lookup a dimensioni accettabili, si possono usare le hugepage.

La relazione tra ramfs e hugepage è che le pagine allocate su ramfs sono appunto hugepage.

Se avete provato, come ho fatto io, a usare ramfs per la buffer cache, vi sarete accorti che solo la buffer cache va a finire su ramfs, mentre la shared pool finisce nella classica shared memory. Per controllare l'allocazione della shared memory basta utilizzare ipcs:
$ ipcs -m

------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0xb0af65c0 1081347 oracle 600 85983232 11
0xb6c83f68 1114116 oracle 600 1629487104 26
0x00000000 1146885 oracle 640 4096 0

Come si può vedere l'utente oracle ha allocato 2 segmenti "grandi" di shared memory: la seconda è la shared pool, impostata a 1504 MB, mentre l'altra è la SGA dell'istanza ASM che gestisce i dischi sulla stessa macchina, mentre non compare la buffer cache.
Le 821 hugepage da 2 MB sul mio sistema di esempio servono ad ospitare la shared pool e la SGA dell'istanza ASM come viene riportato da ipcs.

Le hugepage non sono utili solo nei sistemi a 32 bit. Vedremo le applicazioni nel prossimo post.

26 maggio 2009

Very Large Memory su RHEL5

Ovvero: come fregare il kernel Linux a 32 bit e fare indirizzare a Oracle più di 4 GB di RAM.

Ho appena finito di configurare un antico sistema con processori Intel del 2001, irrimediabilmente limitati a 32 bit e di conseguenza a indirizzare non più di 4 GB di RAM.
Se poi si conta che il kernel Linux si riserva 1 GB di RAM per gli affari propri, in tutto rimane solo circa 1.7 GB di RAM per la SGA (2.7 GB con i kernel "enterprise" e 10gR2 senza relink particolari). Con i kernel "enterprise" di Red Hat, invece, detti hugemem kernel, è possibile indirizzare 4 GB per processo e in totale fino a 64 GB di RAM (!).

Ma se, parafrasando Doc di "Ritorno al Futuro", riuscissimo a installare 8 GB di RAM sul nostro server, potremmo poi utilizzarli effettivamente con Oracle? La risposta è , tramite l'utilizzo del supporto per la Very Large Memory.

Il trucco principale è l'utilizzo di un filesystem per la buffer cache di Oracle (attenzione: non la SGA globalmente, solo la parte a blocchi). Il filesystem è /dev/shm, che è montabile tramite i tipi di filesystem shmfs e tmpfs, oppure col più recente ramfs; la differenza con i primi due tipi è che, mentre per il primo bisogna specificare le dimensioni e per il secondo no, l'ultimo tipo non è swappabile. Fate un df dalla vostra console Linux e probabilmente vedrete montato un filesystem dei tipi che ho appena elencato.

L'istanza Oracle accede a /dev/shm mediante il parametro use_indirect_data_buffers=true allocando alla partenza un file delle dimensioni della buffer cache. Molto importante è notare che, in questo caso, non è più possibile utilizzare l'allocazione automatica delle componenti della SGA e nemmeno cache a blocchi di dimensioni differenti dallo standard predefinito per il database (solitamente 8 KB), quindi bisogna per forza usare il vecchio parametro db_block_buffers e rinunciare ad alcune delle caratteristiche importanti di 9i e 10g.
Ecco un esempio con una buffer cache di 2500 MB:
$ ls -l /dev/shm/
total 2560000
-rw-r----- 1 oracle dba 2621440000 May 25 04:56 ora_BIGGIE_1146885

Legato a questa feature è l'utilizzo delle hugepage, su cui sto ancora indagando e di cui mi occuperò nei prossimi post.

18 maggio 2009

L'acquisizione di MySQL vista da Widenius

Michael "Monty" Widenius, il principale autore di MySQL, ci illustra con un post del suo blog la sua opinione a proposito dell'acquisto di Sun da parte di Oracle, evidenziandone le motivazioni ed elencando le possibili decisioni di Oracle sul futuro di MySQL.
La sua preoccupazione maggiore ora è di mantenere MySQL libero, prevedendo che Oracle difficilmente riuscirà a trattenere i migliori sviluppatori MySQL rimasti dopo l'acquisizione. Widenius cerca ora di riunire sotto un unico consorzio, chiamato Open Database Alliance, tutte le aziende piccole che hanno anche fare con MySQL e che rimangono nell'ambito open-source.

A mio parere Widenius sottovaluta l'esperienza di Oracle nell'open-source: basti pensare anche solo a OCFS2, Enterprise Linux, btrfs. Bisognerà comunque attendere le prossime mosse di Oracle.

10 maggio 2009

ZFS: ancora novità a manetta

Lo sviluppo di ZFS continua a sorprendermi: basta attendere qualche mese ed escono delle novità che si potrebbero definire stupefacenti per qualcosa che è "solo" un filesystem.
In ambiente Solaris le novità tecnologiche si possono provare solo in OpenSolaris, che è la versione free ma anche più avanzata di Solaris.

Ecco le ultime novità su ZFS:
  • Snapshot automatici: necessari in realtà per la feature di time-slider di OpenSolaris.

  • Cache devices: è possibile introdurre un ulteriore livello di cache a disco aggiungendo ai pool dei device che hanno la sola funzione di contenere dati perlopiù a sola lettura. Ciò può aumentare le prestazioni per sistemi a lettura casuale. È interessante che il filesystem stesso decide quali blocchi devono stare in cache e quali no; ZFS ha sicuramente una maggiore cognizione di causa sulla natura dei dati in transito rispetto alla cache del controller. Potenzialmente vengono divise le attività di sola lettura da quelle di scrittura; ciò potrebbe eliminare una eventuale contesa di risorse (le meccaniche dei dischi) in due operazioni molto diverse tra loro, costituendo un enorme vantaggio per molti ambienti.

  • Log del filesystem (ZIL) su device separati: l'intent log di ZFS può essere allocato su device diversi dai dischi che compongono lo storage pool. Per i database è interessante poiché si possono usare dei device molto veloci e di capacità (e costo) limitata per la scrittura sincrona dei dati, tipicamente le chiamate sync() durante la scrittura dei redo log. Questa non è propriamente una novità di per se, ma non ne ho mai fatto menzione.

L'elenco esteso di tutte le novità è reperibile nella pagina apposita del manuale.

07 maggio 2009

Ora come resistere al Mac?

Da ieri è possibile scaricare dal sito Oracle la versione Mac OS X a 64 bit di Oracle 10.2.0.4.

Non so più quale scusa inventarmi per non comprare un Mac nuovo.

A questo punto è probabile che, in futuro, venga rilasciata anche la versione Mac di 11g.

03 maggio 2009

Split-mirror istantaneo con ZFS

Lo scorso ottobre avevo scritto un post sul backup istantaneo con ZFS.
Riprendo quel discorso per estenderlo con le funzionalità aggiuntive fornite da RMAN: tramite RMAN è infatti possibile registrare la copia di backup del datafile, facendola entrare nel catalogo e quindi averla disponibile per qualsiasi tipo di recovery.
RMAN> SQL 'ALTER TABLESPACE USERS BEGIN BACKUP';

$ sudo zfs snapshot u02/oradata@mysnap

Dopo avere effettuato lo snapshot, cataloghiamo la copia del datafile:
RMAN> SQL 'ALTER TABLESPACE USERS END BACKUP';

RMAN> CATALOG DATAFILECOPY '/u02/oradata/.zfs/snapshot/mysnap/MICKEY/users01.dbf';

A questo punto abbiamo una copia recoverable del tablespace in un determinato istante; se vogliamo quindi effettuare modifiche importanti sui dati contenuti in quel tablespace, possiamo considerare lo snapshot praticamente un backup logico, utile nel caso di errore umano. In questo caso i dati in esso contenuti sono recuperabili mediante point-in-time recovery (con EE).
Per il resto, invece, lo snapshot è una copia di backup.
RMAN> list copy of tablespace users;

using target database control file instead of recovery catalog

List of Datafile Copies
Key File S Completion Time Ckp SCN Ckp Time Name
------- ---- - --------------- ---------- --------------- ----
1 4 A 31-MAR-09 403343 31-MAR-09 /u02/oradata/.zfs/snapshot/mysnap/MICKEY/users01.dbf

Mettiamo offline il tablespace:
SQL> alter tablespace users offline normal;

Da RMAN facciamo il restore usando lo split-mirror:
RMAN> restore tablespace users;

Starting restore at 01-MAY-09
allocated channel: ORA_DISK_1
channel ORA_DISK_1: sid=202 devtype=DISK

channel ORA_DISK_1: restoring datafile 00004
input datafile copy recid=1 stamp=682952952 filename=/u02/oradata/.zfs/snapshot/mysnap/MICKEY/users01.dbf
destination for restore of datafile 00004: /u02/oradata/MICKEY/users01.dbf
channel ORA_DISK_1: copied datafile copy of datafile 00004
output filename=/u02/oradata/MICKEY/users01.dbf recid=2 stamp=685963392
Finished restore at 01-MAY-09

SQL> alter tablespace users online;
alter tablespace users online
*
ERROR at line 1:
ORA-01113: file 4 needs media recovery
ORA-01110: data file 4: '/u02/oradata/MICKEY/users01.dbf'

SQL> recover tablespace users;
Media recovery complete.
SQL> alter tablespace users online;

Tablespace altered.

In seguito, quando non ci servirà più il mirror, lo rimuoviamo dal catalogo e cancelliamo lo snapshot ZFS:
RMAN> CHANGE DATAFILECOPY '/u02/oradata/.zfs/snapshot/mysnap/MICKEY/users01.dbf' UNCATALOG;

$ sudo zfs destroy u02/oradata@mysnap

Questo discorso può essere esteso a tutto il database con alter database begin backup (10g). Il backup (o meglio lo snapshot) di database molto grandi diventa quindi un'operazione di pochi secondi, salvo poi copiare le copie dei datafile su altri supporti per altri utilizzi.

28 aprile 2009

Sun difende Solaris rispetto a Linux

Segnalo un interessante whitepaper di Crimson consulting group, dove viene presentato un confronto di esperienze tra utenti enterprise sia di Solaris 10 che di Red Hat Linux, che abbiano numeri "importanti" in termini di fatturato e di parco macchine. Il paragone stato fatto solo su piattaforma x86, dove RHEL è maggiormente affermata.

Gli analisti affermano che Solaris batte Red Hat Linux su costi di acquisizione, supporto, amministrazione, mentre è uguale nei costi di implementazione (installazione).
Essi cercano di dimostrare che, sebbene i costi iniziali sembrino a favore di Linux, a lungo termine ciò perde importanza e non è più vero: alla lunga i costi di RHEL, oltre che a essere più elevati, aumentano anche più rapidamente.

Viene menzionata la necessità di acquistare software aggiuntivo come VMware (opposto ai Containers di Solaris), Veritas (invece di ZFS), supporto per fiberchannel. Viene affermato che il costo del supporto, anche per software terzi, è decisamente maggiore per RHEL. Anche il numero di persone specializzate (sysadmin) viene considerato, come anche la differente difficoltà di amministrazione a livello di singolo server.
Un aspetto interessante è la compatibilità binaria tra differenti release dei sistemi operativi, che è garantita per Solaris. Solaris è inoltre meglio integrato con Java (per evidenti motivi).

Non ho elementi solidi a sostegno delle tesi presentate nella whitepaper, che comunque è una buona lettura per informarsi maggiormente, anche se a mio parere è leggermente di parte.
In più la diffusione di questa pubblicazione, che segue di poche ore l'annuncio dell'acquisizione di Sun da parte di Oracle, sembra un tentativo dell'ultimo momento per tentare di affermare le proprie ragioni e tutelare il proprio patrimonio tecnologico, in vista di probabili interventi sui sistemi operativi da parte di Oracle.

20 aprile 2009

Oracle acquisisce Sun

Oracle ha raggiunto un accordo per l'acquisizione di Sun.

Sui rispettivi siti (Oracle e Sun) ci sono le notizie.

Continuerà lo sviluppo di ZFS?
Alla fine anche la parte residua di MySQL viene acquisita da Oracle.

Oracle ha ora la possibilità di fornire uno stack tecnologico completo, dall'applicazione al disco.

01 aprile 2009

Prova delle novità in OpenSolaris 200811

Con le novità di OpenSolaris 10 200811 le possibilità di gestione dei database Oracle (e non) si moltiplicano e si aprono nuovi scenari di utilizzo.

La novità più divertente è il Time Slider, che può apparire come un gadget invero molto utile ma per PC desktop e notebook, invece si rivela interessantissimo per i DBA, come vedremo presto con la prova con Oracle.

Ora il desktop di Solaris 10 deve sembrare molto simile a quello che avevano in mente alla Apple prima dell'introduzione di Time Machine. Visto che era in previsione l'adozione di ZFS come filesystem universale, cosa che poi non si è verificata, è intuibile che Time Machine sia stato lo sviluppo naturale di un sistema di versioning per i file che sostituisse le immense potenzialità di ZFS, senza però adottarne la tecnologia. I limiti derivanti da questa scelta sono del tutto accettabili per l'utente desktop, ma non per l'utente enterprise.

Time Slider si può impostare tramite il menù System-Administration:

dove si può decidere di quali filesystem (alla ZFS) vengono fatti gli snapshot.
Una volta scelto il default (tutti) il risultato è che viene impostato un attributo del filesystem per l'auto-snapshot:


Una novità ulteriore è lo sviluppo di soluzioni integrate per lo storage di rete SAN e NAS, raggruppate sotto il nome di COMSTAR. Tramite un server Solaris Express o Opensolaris e COMSTAR è possibile costruire una SAN a basso costo (per ora solo Fiberchannel), utilizzando ZFS come storage.
Per maggiorni informazioni su COMSTAR è meglio leggere direttamente il manuale.

Sembra che Sun "sponsorizzi" la ricerca su nuove soluzioni a basso costo tramite la comunità Open Storage.

A breve le prove con Oracle e Time Slider: stay tuned!

26 marzo 2009

Inconsistenze di V$SYSMETRIC_HISTORY

Il patchset 3 di Oracle 10g riserva una strana sorpresa: su alcune installazioni, specialmente RAC, si hanno delle inconsistenze in V$SYSMETRIC_HISTORY.

In V$SYSMETRIC_HISTORY viene raccolto il contenuto di V$SYSMETRIC (le metriche di sistema) ogni 15 secondi, e vengono mantenuti i dati dell'ultima ora.
Periodicamente la view viene salvata su disco in DBA_HIST_SYSMETRIC_HISTORY.

Con queste premesse, è naturale che la differenza tra sysdate (il tempo del server) e il massimo registrato nella colonna END_TIME sia al massimo di 15 secondi. Invece:
SQL> select max(end_time), sysdate from v$sysmetric_history;

MAX(END_TIME) SYSDATE
------------------- -------------------
2009-03-12 14:06:59 2009-03-12 14:10:04

1 row selected.

Qual è la conseguenza di ciò? Càpita che i grafici di Enterprise Manager nella homepage non siano visibili, e ho l'impressione che anche altri fenomeni, come strani grafici di performance, siano collegati a questa anomalia:

Come si può vedere la parte destra del grafico viene disegnata "allungata", come se per quell'intervallo di tempo non ci fossero dati disponibili e quindi il grafico rimanesse costante, mentre sia prima che dopo tutto è regolare.

Trovate maggiori dettagli nel documento Metalink 550083.1, assieme alla descrizione del bug e alla patch relativa, se i sintomi che doveste riscontrare corrispondono a quelli descritti nei documenti Oracle.

20 marzo 2009

Oracle MAA a Milano

Giovedì prossimo sarò all'Oracle Maximum Availability Architecture a Milano.
Potrebbe essere un'occasione per fare due chiacchiere.
Se ci siete, lasciate un commento, così ci si dà un appuntamento per tutti.

Aggiornamento post-evento: eravamo una trentina di persone, a giudicare dai badge la metà di quelle previste. Si è parlato delle novità 11g un po' più da vicino. Abbastanza interessante, c'è discreto materiale per nuovi test, ma la scarsa partecipazione fa pensare.

11 marzo 2009

Il mistero di hangcheck-timer

Recentemente ho aggiornato diversi sistemi con Oracle 10g RAC a 64 bit con RHEL 5.2. In genere, ogni volta che installo un nuovo cluster, scorro velocemente per l'ennesima volta tutti i passi del manuale di installazione, per essere più o meno certo di non avere dimenticato nulla.

Stavolta mi è capitato di notare una stranezza a proposito dell'installazione di Clusterware 10g per Linux nel capitolo "Pre-installation tasks".
Oracle RAC ha bisogno di un modulo del kernel Linux chiamato hangcheck-timer, illustrato nella nota Metalink 726833.1. Questo modulo controlla che il kernel Linux non rimanga bloccato nell'attesa di un blocco (tipo I/O) per più di un determinato periodo di tempo, impostabile con alcuni parametri del modulo; in caso contrario fa il reboot del nodo per evitare lock e inconsistenze più gravi.

La cosa strana è che, controllando i parametri dell'hangcheck-timer, sono risultati diversi da quelli da me impostati su altri cluster già attivi.

Il paragrafo riporta i seguenti parametri per hangcheck-timer:
hangcheck_tick=1 hangcheck_margin=10 hangcheck_reboot=1

mentre su alcuni cluster che gestisco i valori sono i seguenti:
hangcheck_tick=30 hangcheck_margin=180

Mi è sembrato molto strano di aver sbagliato ad impostare quei parametri, specialmente inserendo dei valori precisi che evidentemente erano derivati da qualche documento ufficiale, altrimenti non li avrei messi.

E infatti, con un po' di lavoro di investigazione, anche senza l'aiuto del sito The Wayback Machine temporaneamente non funzionante, sono approdato ad una vecchia versione della pagina di installazione di Clusterware, che riportava i valori da me impostati in passato.
Tutto è confermato anche da questo articolo di OTN.

Non ho molto apprezzato questa variazione senza preavviso dei parametri, quindi ho pensato di essermi perso qualche avviso da qualche parte nel patchset 3, ad esempio.
Non ho trovato alcun riferimento alla variazione dei parametri, tranne nella nota 726833.1, che riporta per 9i i parametri hangcheck_tick=30 hangcheck_margin=180 hangcheck_reboot=1 e per 10g e 11g i valori hangcheck_tick=1 hangcheck_margin=10 hangcheck_reboot=1.

È evidente che qualcosa è cambiato nelle procedure di configurazione di RAC da una certa patchset in poi, e che i parametri nella versione originale dell'installazione erano quelli di 9i, ma non riesco a trovare riferimenti.

Qualcuno di voi ci riesce?

24 febbraio 2009

Ext4, le novità per i database

Recentemente è uscita la versione stabile di ext4, l'evoluzione del noto filesystem di Linux ext3. Chi ha il kernel Linux 2.6.28 o superiore può provare subito le nuove feature.

Leggendo le novità introdotte non si può fare a meno di pensare all'utilizzo con i database. Alcune novità, infatti, sembrano state "ispirate" dal mondo Oracle.

Per ora l'indirizzamento di ext4 è a 48 bit, non a 64 come si potrebbe immaginare, e ciò introduce limiti che, benché difficilmente raggiungibili, sono altrettanto facilmente evitabili; è comunque prevista un'upgrade in futuro.

Molto interessante è l'introduzione degli extent, ovvero insiemi di blocchi contigui che il filesystem considera come un'unica entità, concetto preso direttamente da Oracle.
I vantaggi potrebbero essere: una maggiore velocità, allocazione dello spazio più veloce.
È presente anche la deframmentazione online, che però a mio parere è una soluzione un po' finta a un problema quasi inesistente, in quanto in storage di un certo livello, anche piuttosto basso, i dati vanno a finire in locazioni del disco logico che non hanno relazione fisica tra loro, anche se logicamente contigui. Penso che questo concetto sia alla base della relativamente bassa perdita di performance di ZFS e tutti i filesystem copy-on-write, specialmente quando utilizzati su storage multidisco.
La preallocazione permette di preallocare lo spazio su disco in modo efficiente e contiguo, come fanno ad esempio i programmi di P2P. Per utilizzare questa feature è necessario richiamare una funzione apposita dell'API glibc, quindi bisogna probabilmente attendere che il codice dei database server venga aggiornato.

Ho installato una macchina virtuale di prova con Red Hat 5.3, che dispone di una technology preview di ext4, solo in modalità "dev", quindi comunque non stabile.
Purtroppo constato che non è possibile utilizzare blocchi di dimensione superiore a 4 KB, di fatto impedendo una utile ottimizzazione nei confronti dei blocchi di database di dimensioni maggiori (tipico 8 KB nel caso di Oracle).

Ext4 può essere un significativo passo avanti nella gestione di filesystem grandi e può aiutare sia nella gestione che nella performance dei database, soprattutto Oracle, ma trovo che grandi novità strutturali non ci siano state, almeno non come quelle di ZFS, fin dal suo rilascio.

Per ZFS si preannunciano interessantissime novità, come la possibilità di tornare indietro nel tempo proprio come Time Machine della Apple.

Troveremo il tempo di approfondire tutti questi argomenti.

19 febbraio 2009

Grafici Google, GUI per vari database

Un paio di segnalazioni.

Con Google Charts è possibile ottenere grafici utili per automatizzare il monitoraggio dei propri database.
Di seguito c'è un esempio, generato "live" col caricamento di questa pagina, che illustra l'utilizzo di un grafico per visualizzare l'occupazione relativa su disco dei vari tablespace che compongono un database Oracle:


Il grafico viene generato semplicemente chiamando un URL di Google:
http://chart.apis.google.com/chart?cht=p&chs=400x200&chtt=Database%20Tablespaces%20(MB)&chl=1960|400|250|160|100|5&chd=t:68,14,9,6,3,0&chdl=MGMT_TABLESPACE|SYSTEM|UNDOTBS1|SYSAUX|MGMT_ECM_DEPOT_TS|USERS
passandogli i valori e i tag del grafico. I valori, naturalmente, vengono ricavati da una query sul dizionario dati.
I possibili utilizzi sono limitati solo dalle proprie necessità.
(da un articolo di Alex Gorbachev)


Navicat è un programma utile per la gestione dei propri database MySQL, PostgreSQL e Oracle.
Esiste sia per Windows che per Mac OS X, in varie versioni a pagamento, ma c'è anche la versione gratuita ("Lite").
Ho fatto qualche prova e Navicat mi pare un programma molto semplice, adatto a effettuare le operazioni più comuni.
Le caratteristiche che mi paiono più interessanti sono la possibilità di confrontare schemi e la possibilità di collegarsi a un database tramite un tunnel SSH.

25 gennaio 2009

Il listener che ascoltava troppo

Il listener, per Oracle, è il processo che rimane in ascolto su una porta di rete per le richieste di connessione remote, indirizzandole successivamente al processo server o al dispatcher opportuno.

Il listener può essere controllato tramite la sua console lsnrctl (Listener Control Utility): lo si può fare partire, spegnere e riconfigurare in vari modi, anche senza farlo ripartire, grazie al comando RELOAD.

Con il comando STATUS è possibile avere una panoramica piuttosto dettagliata su che cosa sta facendo il listener:
LSNRCTL> status
Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=myhost)(PORT=1521)))
STATUS of the LISTENER
------------------------
Alias LISTENER
Version TNSLSNR for Linux: Version 10.2.0.3.0 - Production
Start Date 10-JAN-2009 13:21:16
Uptime 12 days 22 hr. 17 min. 45 sec
Trace Level off
Security ON: Local OS Authentication
SNMP OFF
Listener Parameter File /u01/app/oracle/product/10.2.0/network/admin/listener.ora
Listener Log File /u01/app/oracle/product/10.2.0/network/log/listener.log
Listening Endpoints Summary...
(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=myhost)(PORT=1521)))
(DESCRIPTION=(ADDRESS=(PROTOCOL=ipc)(KEY=EXTPROC0)))
Services Summary...
Service "ORA10G" has 1 instance(s).
Instance "ORA10G", status READY, has 1 handler(s) for this service...
Service "ORA10GXDB" has 1 instance(s).
Instance "ORA10G", status READY, has 1 handler(s) for this service...
Service "ORA10G_XPT" has 1 instance(s).
Instance "ORA10G", status READY, has 1 handler(s) for this service...
Service "PLSExtProc" has 1 instance(s).
Instance "PLSExtProc", status UNKNOWN, has 1 handler(s) for this service...
The command completed successfully


L'avvio o la chiusura del listener sono operazioni privilegiate, che quindi richiedono un certo livello di privilegi all'utente. Se ci si collega al server come utente appartenente al gruppo dba o addirittura come utente oracle, i privilegi per dette operazioni vengono automaticamente garantiti. Ciò è infatti visibile nella riga Security ON: Local OS Authentication.

Nelle versioni 9i e precedenti, però, generalmente il controllo di accesso alle operazioni privilegiate è disabilitato:
LSNRCTL> status
Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=IPC)(KEY=EXTPROC)))
STATUS of the LISTENER
------------------------
Alias LISTENER
Version TNSLSNR for Solaris: Version 9.2.0.7.0 - Production
Start Date 27-MAY-2008 14:45:13
Uptime 240 days 19 hr. 51 min. 0 sec
Trace Level off
Security OFF
SNMP OFF
Listener Parameter File
/u01/oracle/product/9207/network/admin/listener.ora
Listener Log File /u01/oracle/product/9207/network/log/listener.log
Listening Endpoints Summary...
(DESCRIPTION=(ADDRESS=(PROTOCOL=ipc)(KEY=EXTPROC)))
(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=ora9ihost)(PORT=1521)))
Services Summary...
Service "PLSExtProc" has 1 instance(s).
Instance "PLSExtProc", status UNKNOWN, has 1 handler(s) for this service...
Service "ORA9I" has 1 instance(s).
Instance "ORA9I", status UNKNOWN, has 1 handler(s) for this service...
Service "ORA9I" has 1 instance(s).
Instance "ORA9I", status READY, has 2 handler(s) for this service...
The command completed successfully

Il problema non è rilevante per quanto riguarda gli utenti locali del server su cui risiedono il database e il listener, in quanto protetti in qualche modo da password, ma perché è possibile controllare remotamente un listener non "blindato" tramite la sua console lsnrctl installata su qualsiasi altra macchina. Esiste infatti la possibilità di specificare il current_listener; vediamolo in opera utilizzando il comando version (ma status va benissimo):
LSNRCTL> version
Connecting to
(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=myhost)(PORT=1521)))
TNSLSNR for Linux: Version 10.2.0.3.0 - Production
TNS for Linux: Version 10.2.0.3.0 - Production
Unix Domain Socket IPC NT Protocol Adaptor for Linux: Version 10.2.0.3.0 - Production
Oracle Bequeath NT Protocol Adapter for Linux: Version 10.2.0.3.0 - Production
TCP/IP NT Protocol Adapter for Linux: Version 10.2.0.3.0 - Production,,
The command completed successfully
LSNRCTL> set current_listener ora9ihost
Current Listener is ora9ihost
LSNRCTL> version
Connecting to
(DESCRIPTION=(CONNECT_DATA=(SERVICE_NAME=ora9ihost))(ADDRESS=(PROTOCOL=TCP)(HOST=ora9ihost)(PORT=1521)))
TNSLSNR for Solaris: Version 9.2.0.7.0 - Production
TNS for Solaris: Version 9.2.0.7.0 - Production
Unix Domain Socket IPC NT Protocol Adaptor for Solaris: Version 9.2.0.7.0 - Production
Oracle Bequeath NT Protocol Adapter for Solaris: Version 9.2.0.7.0 - Production
TCP/IP NT Protocol Adapter for Solaris: Version 9.2.0.7.0 - Production,,
The command completed successfully

Il comando VERSION fornisce le informazioni per qualsiasi database (e già queste informazioni a disposizione di tutti potrebbero essere inopportune), ma a noi interessa che con il listener 9i non opportunamente configurato è possibile a questo punto scrivere "stop" e il listener viene spento su ora9ihost.
Per 10g invece questa "feature" viene disabilitata di default, quindi cercando di fermare il listener remotamente si ottiene:
LSNRCTL> stop
Connecting to
(DESCRIPTION=(CONNECT_DATA=(SERVICE_NAME=ora10g))(ADDRESS=(PROTOCOL=TCP)(HOST=ora10g)(PORT=1521)))
TNS-01189: The listener could not authenticate the user

Inoltre, con un listener "aperto", impostando log_directory, log_file e log_status si può sovrascrivere qualsiasi file accessibile all'utente con cui gira il listener a cui ci si è collegati (meglio non immaginare che cosa è possibile fare).

In 10g, per il listener è attivo di default (ON) il nuovo parametro LOCAL_OS_AUTHENTICATION_<listener>, quindi non ci si deve preoccupare più di tanto; con il parametro impostato a "OFF" il listener si comporta come su 9i. In tal caso (10g aperto, 9i e precedenti) è opportuno impostare la password per il listener con il comando CHANGE_PASSWORD.
Una volta impostata la password, per eseguire qualsiasi operazione privilegiata è necessario prima autenticarsi con SET PASSWORD.

06 gennaio 2009

Un'interconnect RAC efficiente

Com'è noto un cluster Oracle è formato da più istanze che montano lo stesso database condiviso.
Ogni istanza ha una propria buffer cache che si gestisce autonomamente, in più esiste una caratteristica di RAC chiamata cache fusion, che sarebbe un insieme di processi che si parlano tra loro da un nodo del cluster all'altro, garantendo l'integrità dei dati attraverso le istanze.

Dato che una query, eseguita su un nodo qualunque del cluster, deve ottenere risultati consistenti, le istanze devono poter parlare tra loro tramite una connessione molto veloce, che in genere viene ottenuta tramite una rete ethernet, soprattutto per la relativa economicità della soluzione.
Le istanze trasferiscono tra loro direttamente i blocchi della SGA, che vengono quindi impacchettati secondo gli usuali protocolli di rete per essere spediti alle altre istanze del cluster. Oracle usa UDP per il trasferimento, penso soprattutto per evitare l'overhead del TCP.
La dimensione dei pacchetti trasferiti sull'interconnect è data al parametro della scheda di rete MTU, che è la dimensione in byte del più grande pacchetto o frame che può essere inviato attraverso la rete. In generale il default per tutte le interfacce di rete è 1500 byte.
Ciò implica che, trasferendo un blocco dalla SGA di un nodo a quella di un altro nodo, il blocco viene spezzettato in più frame sufficienti a contenere le sue dimensioni (es. 8K standard). Questa operazione è relativamente lenta, e poiché le prestazioni dell'interconnect possono avere un forte impatto sulle prestazioni globali del cluster, è preferibile limitare l'uso dell'interconnect, facendo in modo ad esempio che qualsiasi nodo del cluster lavori prevalentemente o unicamente su un sottoinsieme dei dati disgiunto da quello su cui lavora ciascun altro nodo.

C'è anche un modo per aumentare le prestazioni dell'interconnect: l'uso dei jumbo frame.
I jumbo frame sono dei frame (molto) più grandi di quelli standard di 1500 bytes; generalmente il limite delle loro dimensioni è 9000 bytes; questo limite è interessante perché molto vicino alle dimensioni "standard" del blocco Oracle (8K), e comunque superiore alle dimensioni dei blocchi usati in OLTP (2K, 4K, 8K).

L'idea che mi è venuta è di verificare che i blocchi transitino "intatti" sulla interconnect, in modo da massimizzare le prestazioni, al variare della MTU. Non tutte le apparecchiature di rete, infatti, supportano i jumbo frame o comunque dimensioni troppo grandi dei frame.
Il modo più diretto e anche più affidabile è usare un tool di monitoring del traffico. Io propendo per Wireshark, per la facilità e l'estrema potenza.

Il test che ho pensato è il seguente: creo una tabella 3 o 4 blocchi in un tablespace con blocksize di 8K, inserisco un po' di dati ad-hoc, "carico" il suo contenuto nella SGA dell'istanza su cui sto lavorando (una semplice select va bene, anche se l'insert ordinaria dovrebbe bastare), e poi cerco di accedere agli stessi dati da una seconda istanza del cluster (un'altra select è sufficiente).
Ipotizzo che Oracle trasferisca i blocchi in questione sull'interconnect invece che leggerli da disco, poiché si presume che il trasferimento via interconnect sia molto più veloce.

Per riconoscere i blocchi nel traffico di rete utilizzo dei "marker" sui dati: creo una tabella contenente stringhe ben visibili. In questo caso utilizzo stringhe tipo "AAAA...", "BBBB...." e così via, per 20 righe di circa 2000 caratteri ciascuna, in modo che in ogni blocco da 8192 byte ci stiano 3/4 record. "Forzo" la massima occupazione dello spazio nei blocchi utilizzando l'opzione pctfree 0.
SQL> create table aob (rn integer, rp char(2000)) pctfree 0;

Table created.

SQL> insert into aob select rownum, rpad(chr(64+rownum), 2000, chr(64+rownum))
from all_objects where rownum <= 20;

20 rows created.

SQL> commit;

Commit complete.

SQL> select rn, substr(rp, 1, 1) letter, length(rp) len, rowid
from aob order by rn;

RN LETT LEN ROWID
---------- ---- ---------- ------------------
1 A 2000 AAAM8OAAFAAABCXAAA
2 B 2000 AAAM8OAAFAAABCXAAB
3 C 2000 AAAM8OAAFAAABCXAAC
4 D 2000 AAAM8OAAFAAABCXAAD
5 E 2000 AAAM8OAAFAAABCYAAA
6 F 2000 AAAM8OAAFAAABCYAAB
7 G 2000 AAAM8OAAFAAABCYAAC
8 H 2000 AAAM8OAAFAAABCYAAD
9 I 2000 AAAM8OAAFAAABCUAAA
10 J 2000 AAAM8OAAFAAABCUAAB
11 K 2000 AAAM8OAAFAAABCUAAC
12 L 2000 AAAM8OAAFAAABCUAAD
13 M 2000 AAAM8OAAFAAABCVAAA
14 N 2000 AAAM8OAAFAAABCVAAB
15 O 2000 AAAM8OAAFAAABCVAAC
16 P 2000 AAAM8OAAFAAABCVAAD
17 Q 2000 AAAM8OAAFAAABCWAAA
18 R 2000 AAAM8OAAFAAABCWAAB
19 S 2000 AAAM8OAAFAAABCWAAC
20 T 2000 AAAM8OAAFAAABCWAAD

20 rows selected.

Nell'ultima select per brevità è presente solo una lettera per ogni riga (rp è una stringa char di 2000 caratteri tutti uguali); è evidente come i record stiano nello stesso blocco a gruppi di 4 dal rowid: la riga all'interno del blocco è indetificata dagli ultimi tre caratteri del rowid, mentre il blocco dai 5 caratteri precedenti.
Ho quindi un modo per identificare visualmente i blocchi in transito sull'interconnect: il primo sarà pieno di A, B, C e D; il secondo di E, F, G, H, e così via.

Dopo aver eseguito le istruzioni precedenti su un nodo del cluster, passo al successivo facendo semplicemente
SQL> select * from aob;

Nel frattempo ho attivato Wireshark in modo da loggare tutto il traffico di passaggio sull'interfaccia di rete dell'interconnect su uno dei due nodi del cluster.


Ecco che cosa si vede da Wireshark con la MTU a 1500 byte (il blocco visualizzato è evidenziato in blu nell'elenco dei frame):

I frame di dati sono lunghi 882 byte, e vengono riassemblati in blocchi da 8248 byte (vedere in basso a sinistra nell'immagine). Nell'immagine si vede uno dei frame contenenti la "P"; servono perfino più di due frame per poter trasmettere anche una sola riga, lunga almeno 2002 byte.
Il frame di 1500 byte è quindi addirittura di gran lunga sottoutilizzato in questo caso.


Ora vediamo in dettaglio il traffico con la MTU a 9000 byte.
Ecco il blocco che comincia con la riga delle "I":
Più in basso si passa dalla "K" alla "L":
Ecco il blocco della "A" (notare che non sono in ordine, giustamente):
Ecco la fine del blocco della "A", "D":
Nei dati del blocco è presente la dimensione del frame, 8282 byte:
Ciò significa che, con l'MTU impostata a 9000 byte, i blocchi transitano integri sull'interconnect, garantendone il massimo delle prestazioni.
Probabilmente è possibile arrivare ad un valore ottimale per l'MTU, di poco superiore o di poco inferiore, guardando anche gli altri tipi di comunicazione sull'interconnect, ma per ora ci fermiamo qui.

21 dicembre 2008

Analytics - n.4: ranking

Stavolta ci occupiamo delle funzioni RANK e DENSE_RANK.

Consideriamo ancora l'esempio dei maratoneti.
All'arrivo possiamo fare la classifica generale, ordinando per tempo di arrivo e assegnando a ogni corridore un numero progressivo che parte da 1, ad esempio con ROWNUM. Prendo un sottoinsieme della mia tabella di esempio per semplicità:
SQL> select ri, at, rownum rn from
2 (select runner_id ri, arrival_time at
3 from marathon
4 where arrival_time between to_date('2008-06-19 13:01:00', 'YYYY-MM-DD HH24:MI:SS')
5 and to_date('2008-06-19 13:03:00', 'YYYY-MM-DD HH24:MI:SS') order by arrival_time)
6 ;

RI AT RN
---------- ------------------- ----------
2015 2008-06-19 13:01:04 1
2293 2008-06-19 13:01:05 2
3895 2008-06-19 13:01:06 3
4931 2008-06-19 13:01:07 4
4810 2008-06-19 13:01:10 5
2747 2008-06-19 13:01:17 6
125 2008-06-19 13:01:19 7
1664 2008-06-19 13:01:20 8
632 2008-06-19 13:01:21 9
638 2008-06-19 13:01:23 10
2207 2008-06-19 13:01:31 11
4816 2008-06-19 13:01:32 12
3550 2008-06-19 13:01:33 13
3632 2008-06-19 13:01:33 14
309 2008-06-19 13:01:34 15
2254 2008-06-19 13:01:36 16
1671 2008-06-19 13:01:43 17
2068 2008-06-19 13:01:46 18
3167 2008-06-19 13:02:02 19
4269 2008-06-19 13:02:02 20
4962 2008-06-19 13:02:02 21
3921 2008-06-19 13:02:02 22
2298 2008-06-19 13:02:29 23
4933 2008-06-19 13:02:30 24
2093 2008-06-19 13:02:34 25
1528 2008-06-19 13:02:36 26
1779 2008-06-19 13:02:40 27
4313 2008-06-19 13:02:40 28
2547 2008-06-19 13:02:40 29
4482 2008-06-19 13:02:43 30
3667 2008-06-19 13:02:44 31
1117 2008-06-19 13:02:51 32
4393 2008-06-19 13:02:53 33
591 2008-06-19 13:02:55 34

34 rows selected.

Ma che cosa succede se due corridori hanno lo stesso tempo di arrivo (es. 13:02:02)? Bisogna assegnargli la posizione a parimerito. La funzione DENSE_RANK ci viene in aiuto (colonna DR):
SQL> select ri, at, rownum rn, dense_rank() over (order by at) dr from
2 (select runner_id ri, arrival_time at
3 from marathon
4 where arrival_time between to_date('2008-06-19 13:01:00', 'YYYY-MM-DD HH24:MI:SS')
5 and to_date('2008-06-19 13:03:00', 'YYYY-MM-DD HH24:MI:SS') order by arrival_time)
6 ;

RI AT RN DR
---------- ------------------- ---------- ----------
2015 2008-06-19 13:01:04 1 1
2293 2008-06-19 13:01:05 2 2
3895 2008-06-19 13:01:06 3 3
4931 2008-06-19 13:01:07 4 4
4810 2008-06-19 13:01:10 5 5
2747 2008-06-19 13:01:17 6 6
125 2008-06-19 13:01:19 7 7
1664 2008-06-19 13:01:20 8 8
632 2008-06-19 13:01:21 9 9
638 2008-06-19 13:01:23 10 10
2207 2008-06-19 13:01:31 11 11
4816 2008-06-19 13:01:32 12 12
3550 2008-06-19 13:01:33 13 13
3632 2008-06-19 13:01:33 14 13
309 2008-06-19 13:01:34 15 14
2254 2008-06-19 13:01:36 16 15
1671 2008-06-19 13:01:43 17 16
2068 2008-06-19 13:01:46 18 17
3167 2008-06-19 13:02:02 19 18
4269 2008-06-19 13:02:02 20 18
4962 2008-06-19 13:02:02 21 18
3921 2008-06-19 13:02:02 22 18
2298 2008-06-19 13:02:29 23 19
4933 2008-06-19 13:02:30 24 20
2093 2008-06-19 13:02:34 25 21
1528 2008-06-19 13:02:36 26 22
1779 2008-06-19 13:02:40 27 23
4313 2008-06-19 13:02:40 28 23
2547 2008-06-19 13:02:40 29 23
4482 2008-06-19 13:02:43 30 24
3667 2008-06-19 13:02:44 31 25
1117 2008-06-19 13:02:51 32 26
4393 2008-06-19 13:02:53 33 27
591 2008-06-19 13:02:55 34 28

34 rows selected.

Diverso sarebbe il caso in cui volessimo considerare l'ordine di arrivo, fatti salvi i corridori a parimerito: infatti quelli a parimerito sarebbero tutti nella stessa posizione, mentre il successivo avrebbe comunque la posizione assoluta (tipo ROWNUM) conservata (colonna R):
SQL> select ri, at, rownum rn, dense_rank() over (order by at) dr,
2 rank() over (order by at) r from
3 (select runner_id ri, arrival_time at
4 from marathon
5 where arrival_time between to_date('2008-06-19 13:01:00', 'YYYY-MM-DD HH24:MI:SS')
6 and to_date('2008-06-19 13:03:00', 'YYYY-MM-DD HH24:MI:SS') order by arrival_time)
7 ;

RI AT RN DR R
---------- ------------------- ---------- ---------- ----------
2015 2008-06-19 13:01:04 1 1 1
2293 2008-06-19 13:01:05 2 2 2
3895 2008-06-19 13:01:06 3 3 3
4931 2008-06-19 13:01:07 4 4 4
4810 2008-06-19 13:01:10 5 5 5
2747 2008-06-19 13:01:17 6 6 6
125 2008-06-19 13:01:19 7 7 7
1664 2008-06-19 13:01:20 8 8 8
632 2008-06-19 13:01:21 9 9 9
638 2008-06-19 13:01:23 10 10 10
2207 2008-06-19 13:01:31 11 11 11
4816 2008-06-19 13:01:32 12 12 12
3550 2008-06-19 13:01:33 13 13 13
3632 2008-06-19 13:01:33 14 13 13
309 2008-06-19 13:01:34 15 14 15
2254 2008-06-19 13:01:36 16 15 16
1671 2008-06-19 13:01:43 17 16 17
2068 2008-06-19 13:01:46 18 17 18
3167 2008-06-19 13:02:02 19 18 19
4269 2008-06-19 13:02:02 20 18 19
4962 2008-06-19 13:02:02 21 18 19
3921 2008-06-19 13:02:02 22 18 19
2298 2008-06-19 13:02:29 23 19 23
4933 2008-06-19 13:02:30 24 20 24
2093 2008-06-19 13:02:34 25 21 25
1528 2008-06-19 13:02:36 26 22 26
1779 2008-06-19 13:02:40 27 23 27
4313 2008-06-19 13:02:40 28 23 27
2547 2008-06-19 13:02:40 29 23 27
4482 2008-06-19 13:02:43 30 24 30
3667 2008-06-19 13:02:44 31 25 31
1117 2008-06-19 13:02:51 32 26 32
4393 2008-06-19 13:02:53 33 27 33
591 2008-06-19 13:02:55 34 28 34

34 rows selected.

Un'altra funzione è ROW_NUMBER, che semplicemente assegna un valore crescente a partire da 1 alle righe della partizione, nulla più.
In pratica potremmo sostituire la subquery più interna che fa l'ordinamento direttamente con ROW_NUMBER, senza utilizzare ROWNUM:
SQL> select runner_id ri, arrival_time at, row_number() over (order by arrival_time) rn
2 from marathon
3 where arrival_time between to_date('2008-06-19 13:01:00', 'YYYY-MM-DD HH24:MI:SS')
4 and to_date('2008-06-19 13:03:00', 'YYYY-MM-DD HH24:MI:SS') order by arrival_time;

RI AT RN
---------- ------------------- ----------
2015 2008-06-19 13:01:04 1
2293 2008-06-19 13:01:05 2
3895 2008-06-19 13:01:06 3
4931 2008-06-19 13:01:07 4
4810 2008-06-19 13:01:10 5
2747 2008-06-19 13:01:17 6
125 2008-06-19 13:01:19 7
1664 2008-06-19 13:01:20 8
632 2008-06-19 13:01:21 9
638 2008-06-19 13:01:23 10
2207 2008-06-19 13:01:31 11
4816 2008-06-19 13:01:32 12
3550 2008-06-19 13:01:33 13
3632 2008-06-19 13:01:33 14
309 2008-06-19 13:01:34 15
2254 2008-06-19 13:01:36 16
1671 2008-06-19 13:01:43 17
2068 2008-06-19 13:01:46 18
3167 2008-06-19 13:02:02 19
4269 2008-06-19 13:02:02 20
4962 2008-06-19 13:02:02 21
3921 2008-06-19 13:02:02 22
2298 2008-06-19 13:02:29 23
4933 2008-06-19 13:02:30 24
2093 2008-06-19 13:02:34 25
1528 2008-06-19 13:02:36 26
1779 2008-06-19 13:02:40 27
4313 2008-06-19 13:02:40 28
2547 2008-06-19 13:02:40 29
4482 2008-06-19 13:02:43 30
3667 2008-06-19 13:02:44 31
1117 2008-06-19 13:02:51 32
4393 2008-06-19 13:02:53 33
591 2008-06-19 13:02:55 34

34 rows selected.

02 dicembre 2008

Indovinello con COUNT: soluzione

L'indovinello e la relativa soluzione derivano dal libro di Jonathan Lewis "Cost-based Oracle", di cui ho già parlato; libro peraltro a volte molto difficile, quasi sempre molto pesante, che però a volte riserva soprese e piccole grandi illuminazioni.
Ho riscritto tutto in un modo che mi sembra più chiaro.

Cominciamo a definire un vettore contenente tutte le età possibili da 1 a 36 anni:

select
rownum age
from
all_objects
where
rownum <= 36;

Cominciamo a porre la prima condizione: il prodotto delle età è 36.
Imponiamo anche, per comodità, la condizione che le età siano crescenti (age1 <= age2 <= age3), per evitare risultati simmetrici:
with age_list as
(
select
rownum age
from
all_objects
where
rownum <= 36
)
select
age1.age a1,
age2.age a2,
age3.age a3
from
age_list age1,
age_list age2,
age_list age3
where
age1.age <= age2.age
and age2.age <= age3.age
and age1.age * age2.age * age3.age = (select max(age) from age_list);

Ora abbiamo tutte le età possibili:
 A1    A2       A3
---------- ---------- ----------
1 1 36
2 2 9
1 2 18
3 3 4
2 3 6
1 3 12
1 4 9
1 6 6

Ora sommiamo le età:
with age_list as
(
select
rownum age
from
all_objects
where
rownum <= 36
),
product_check as
(
select
age1.age a1,
age2.age a2,
age3.age a3
from
age_list age1,
age_list age2,
age_list age3
where
age1.age <= age2.age
and age2.age <= age3.age
and age1.age * age2.age * age3.age = (select max(age) from age_list)
)
select
a1,
a2,
a3,
a1+a2+a3 age_sum
from product_check;

A1 A2 A3 AGE_SUM
---------- ---------- ---------- ----------
1 1 36 38
2 2 9 13
1 2 18 21
3 3 4 10
2 3 6 11
1 3 12 16
1 4 9 14
1 6 6 13

Ora: il secondo DBA è ancora dubbioso anche sapendo la somma delle età, ottenuta contando le persone presenti nella stanza, quindi ci troviamo in un caso di ambiguità (somme uguali), che troviamo mediante la nostra funzione analitica :-)
with age_list as
(
select
rownum age
from
all_objects
where
rownum <= 36
),
product_check as
(
select
age1.age a1,
age2.age a2,
age3.age a3
from
age_list age1,
age_list age2,
age_list age3
where
age1.age <= age2.age
and age2.age <= age3.age
and age1.age * age2.age * age3.age = (select max(age) from age_list)
),
sum_check as
(
select
a1,
a2,
a3,
a1+a2+a3 age_sum
from
product_check
)
select
a1,
a2,
a3,
count(*) over (partition by age_sum) c
from
sum_check;

A1 A2 A3 C
---------- ---------- ---------- ----------
3 3 4 1
2 3 6 1
2 2 9 2
1 6 6 2
1 4 9 1
1 3 12 1
1 2 18 1
1 1 36 1

I due casi sono 2, 2, 9 e 1, 6, 6, dove il conteggio delle somme uguali è maggiore di 1.
L'ultimo indizio ci informa che esiste una figlia maggiore (il criceto serve a confondere un po' le idee), il che ci permette di escludere che il caso 1, 6, 6 e di affermare quindi che le età sono 2, 2 e 9 anni.

30 novembre 2008

Analytics - n.3: un indovinello con COUNT

Anche le funzioni classiche come COUNT, SUM, AVG hanno la loro controparte analitica.

Oggi utilizziamo Oracle per risolvere un indovinello, utilizzando la versione analitica delle tradizionali funzioni di gruppo. Chi lo dice che tutte le spiegazioni devono per forza essere noiose? :-)

Due DBA si incontrano all'Oracle Openworld. Il primo viene a sapere che il secondo ha 3 figlie, quindi gli chiede la loro età. Il secondo, in vena di enigmi, dice:
"Il prodotto delle età delle mie figlie è 36."
L'altro ragiona un attimo, ma chiede più informazioni, perché il solo prodotto delle età non gli basta.
"La somma delle età delle mie figlie è uguale al numero di persone che stanno in questa stanza"
L'altro conta le persone, ci pensa, ma i dati non gli bastano ancora, e chiede maggiori indizi.
"La più grande delle mie figlie ha un criceto maculato".
L'altro raggiunge un prompt SQL e, dopo pochi minuti, ha la soluzione!

Come avrà fatto?

Domani pubblicherò la soluzione.
Mi raccomando non utilizzate Google, altrimenti non è leale (sempre che si riesca a trovare con Google).
Pubblicate le vostre soluzioni nei commenti, a condizione che utilizziate SQL e le funzioni analitiche.

23 novembre 2008

Solaris sostituisce Linux a casa

Sono passato a Solaris per il mio megaserver casalingo. Complice la scarsa affidabilità del paio di dischi originali, sostituiti con una coppia da 320 GB nuova, mi sono trovato a provare la nuova splendida versione di Solaris (10/08) su x86/64.

Con la nuova versione di Solaris è possibile avere il disco di root in mirror software, attraverso un pool ZFS. Non solo: il programma di installazione installa il boot-loader grub anche sul secondo disco, rendendo praticamente indifferente il disco da cui viene fatto il boot.
ZFS infatti si basa su metadati registrati sui dischi; da ciò ne deriva che, anche facendo il boot dal secondo disco, ci si ritrova esattamente come se si fosse fatto il boot dal primo.
Non ho ancora provato ma, quasi sicuramente, anche l'ordine dei dischi rispetto al BIOS e la loro posizione sul bus SATA sia del tutto indifferente.

Con ZFS lo spazio dei dischi si sfrutta molto meglio, non essendoci spazi vuoti tra un filesystem e l'altro, quindi mi sento un po' meno "costretto" dallo spazio disponibile.
ZFS inoltre ottimizza le letture da disco, utilizzando entrambi i dischi in parallelo come i migliori controller RAID, ottenendo quasi un raddoppio delle prestazioni. Anche in scrittura la velocità non manca, almeno da quanto ho potuto constatare. Pensavo infatti che la doppia scrittura fosse molto penalizzante, ma mi sono ricreduto; evidentemente ZFS fa la seconda scrittura in modo asincrono.

Pochi giorni fa è uscito l'ultimo patchset 10g per Solaris x86/64, che ora fa bella mostra di sé nel nuovo server. Purtroppo per 11g si prevedono tempi più lunghi, sempre che non decidano (chissà) di trascurare Solaris su Intel.

VirtualBox, il software di virtualizzazione open-source della Sun, funziona perfettamente, e per ora sto provando a creare nuove macchine virtuali Linux, per avere installazioni RAC di prova.

ZFS esporta nativamente target iSCSI tramite volumi ZFS, differenti dai filesystem ZFS, quindi penso che i nuovi RAC virtuali avranno il loro storage condiviso via iSCSI. Non so invece se sono utilizzabili con RAC i volumi vboxfs condivisi, per i soliti problemi di sincronia dell'accesso ai dischi.

Ho completato anche una installazione di Oracle BI Publisher (per una demo) in modalità manuale, ovvero facendo uso del servlet engine di Sun, invece del nativo OC4J, non disponibile per la mia coppia CPU/OS.
Ho utilizzato infatti Sun Java System Web Server 7.0, molto interessante. Ovviamente l'installazione del web server è stata facilissima, come la sua gestione (se si conosce ciò che si sta facendo). In questi aspetti, il software enterprise è enormemente più piacevole da usare del software open-source.

Ho fatto i primi passi anche con i Solaris Containers, che sono una specie di macchine virtuali uguali al sistema operativo ospite. Di questo parleremo più avanti.

19 novembre 2008

Oracle e SQL Server a confronto

Avendo avuto a che fare con un test di migrazione da Microsoft SQL Server a Oracle, mi si è aperto un mondo nuovo con l'utilizzo di SQL Developer Migration Workbench.

Nella documentazione HTML è presente un'interessante comparativa tra SQL Server e Oracle, molto istruttiva da qualsiasi punto di vista, anche per riassumere le caratteristiche principali dei due database.
In particolare vengono sintetizzate le differenze per quanto riguarda i concetti di locking e di concorrenza.

04 novembre 2008

I servizi RAC anche su istanza singola

Una delle particolarità di Oracle RAC è la possibilità di definire i servizi, ovvero una sorta di modalità di connessione via rete al db avente caratteristiche definibili dall'utente.
Se, per esempio, abbiamo un RAC da 3 nodi e volessimo che una nostra applicazione si collegasse solo ai nodi 2 e 3 del cluster, definiremmo un servizio che viene attivato solo sui nodi 2 e 3, in modo che le connessioni dirette al cluster vengano rimandate dai listener solo ai nodi su cui è attivo il servizio.
Ci sono molti altri parametri di configurazione di un servizio, tra cui le istanze verso le quali viene rediretto il servizio nel caso di indisponibilità dei nodi attivi, timeout vari e quant'altro.

È abbastanza interessante però che si possano definire i servizi anche per un database non RAC, composto da una sola istanza (esistono comunque anche i RAC single-instance).
I servizi, nel caso istanza singola, si possono creare e gestire con DBMS_SERVICE e le sue procedure CREATE_SERVICE e START_SERVICE.
CREATE_SERVICE prende come parametri il nome del servizio visto dal lato db e il nome visto dal lato listener, ovvero il famoso SERVICE_NAME all'interno dei connect descriptor dei client.

Ovviamente il connection load-balancing, e i vari metodi di failover non hanno senso, ma è possibile trarre qualche vantaggio da questa architettura.
Direi che i vantaggi possono essere un paio:
  1. Se creo un servizio e gli utenti di un certo gruppo/applicazione si collegano a quel servizio, posso fermarlo per manutenzione quando mi pare e impedirgli di connettersi, mentre il db rimane aperto e gli altri utenti continuano a lavorare senza problemi.
  2. Tramite la procedura DISCONNECT_SESSION posso disconnettere tutte e sole le sessioni collegate ad un determinato servizio con un solo comando, e a volte può essere veramente utile.

18 ottobre 2008

Backup "istantaneo" con Oracle e ZFS

Continuiamo la miniserie di post su ZFS, l'ottimo filesystem di Solaris e OpenSolaris.

La proprietà di poter fare lo snapshot di un filesystem consente di utilizzare il backup a caldo del database via ALTER DATABASE BEGIN BACKUP con Oracle 10g.
In Oracle 10g, infatti, è possibile mettere tutto il db in backup mode con un solo comando, invece di farlo singolarmente per ogni tablespace.

Ecco il log di un backup effettuato a caldo con uno snapshot su una macchina Solaris 10:
Connected to:
Oracle Database 10g Release 10.2.0.4.0 - 64bit Production

SQL> set timing on
SQL> alter database begin backup;

Database altered.

Elapsed: 00:00:00.62

SQL> host sudo zfs destroy u01/oradata@`date +%A`
cannot open 'u01/oradata@Thursday': dataset does not exist

SQL> host sudo zfs snapshot u01/oradata@`date +%A`

SQL> alter database end backup;

Database altered.

Elapsed: 00:00:00.44
SQL>

Il tutto è durato pochi secondi, e comunque la durata dello snapshot è quasi indipendente dalle dimensioni del database.
Lo snapshot è ora pronto su disco da copiare su un altro server. Questo è il passo che richiederà più tempo, ma il backup è praticamente già fatto.

$ zfs list
NAME USED AVAIL REFER MOUNTPOINT
u01 17.2G 49.7G 80.6M /u01
u01/oradata 17.1G 49.7G 17.1G /u01/oradata
u01/oradata@Thursday 526K - 17.1G -
u02 16.9G 79.6G 14.3G /u02
u02/oradata 2.59G 79.6G 2.59G /u02/oradata

Per il trasferimento dello snapshot su nastro o su altri dischi o server si possono usare i comandi tradizionali, oppure "rigenerare" lo snapshot direttamente su un altro filesystem ZFS di un altro server via ssh con zfs send.
Ma vi lascio scoprire questa magica e gustosa feature da soli per esercizio :-)

Come è possibile tutto ciò?
Come si può vedere lo snapshot, dopo qualche secondo dalla creazione, occupa circa 500 KB, e le sue dimensioni continuano a crescere.
ZFS alloca lo spazio su disco in "record" (analoghi ai blocchi dei filesystem tradizionali), grandi di default 128 KB.
In pratica ZFS fa in modo di tenere validi su disco i record appartenenti al filesystem al momento dello snapshot, occupando nuovo spazio per i blocchi che vengono modificati o aggiunti al filesystem originario. Il nuovo spazio allocato, man mano che passa il tempo, compare nella lista come occupazione dello snapshot. Ricorda il funzionamento degli snapshot LVM. Geniale.

08 ottobre 2008

RAC su macchine virtuali: ma ha senso?

Leggo volentieri da Techblast che Oracle ha certificato RAC su macchine virtuali Oracle VM.

Avere un cluster su macchine virtuali va un po' contro l'idea di alta affidabilità insita nel concetto di cluster.

La parte interessante nel documento Oracle è ovviamente l'architettura. E le chicche non mancano.

In sintesi l'architettura è spiegata dall'immagine seguente:
È evidente come la virtualizzazione venga utilizzata per ospitare più nodi virtuali (di cluster diversi) su un solo server fisico, ma il meccanismo di alta affidabilità viene conservato poichè ogni nodo di uno stesso database cluster risiede su un server fisico diverso.

Lo storage condiviso di RAC viene gestito in domain0:
Il multipathing viene utilizzato solamente sul percorso fisico dei dati da e verso i dischi, non su quello virtuale da domain0 ai domini virtuali.

Analogamente, le interfacce di rete sono in bond solo sulle porte fisiche, mentre i bridge virtuali di xenbr0 e xenbr1, sempre in domain0, sono rispettivamente la rete pubblica e la rete privata del RAC.
Essendo le istanze del RAC su nodi fisici differenti, anche per la comunicazione su rete privata si passa da rete fisica; ciò non è necessario invece nel caso di nodi sullo stesso server, che comunicherebbero direttamente attraverso il bridge 1; ma questa non è assolutamente una configurazione ragionevole per un ambiente HA.

Date le premesse del documento Oracle, sotto quest'ottica la soluzione RAC su macchine virtuali diventa tutt'altro che insensata in vari ambienti, soprattutto per consolidare server e sfruttare al massimo la potenza di elaborazione dei server attuali, facendo economia di scala.

25 settembre 2008

Analytics - n.2: LEAD e LAG

LEAD e LAG sono funzioni analitiche che permettono di accedere al valore di un campo di un record del recordset risultato della nostra query, specificando l'offset dal record in cui la funzione viene valutata.
Nel caso di un offset che va fuori dal recordset, è possibile specificare un valore di default ritornato dalla funzione.

Il punto essenziale, come per gran parte delle funzioni analitiche, è l'ordinamento del risultato: il valore dell'n-esima riga prima o dopo la riga corrente è dato proprio dell'"order by" specificato nel partizionamento del resultset.

Prendiamo sempre l'esempio dei maratoneti che ho usato in un post precedente.
Mettiamo che io voglia conoscere la differenza di tempo in arrivo tra un corridore e il successivo, un po' come viene fatto alla fine di gare a tempo.
Posso usare la funzione LAG con parametro 1 e computata ordinando la tabella per arrival_time:
 SELECT runner_id,
arrival_time ,
extract(minute FROM
(arrival_time - lag(arrival_time, 1, arrival_time) over (order by arrival_time)) DAY TO second)
||''''
|| extract(second FROM
(arrival_time - lag(arrival_time, 1, arrival_time) over (order by arrival_time)) DAY TO second)
||'"' time_diff
FROM marathon
WHERE rownum < 10
ORDER BY arrival_time;

Nell'esempio prendo solo 10 righe per avere un po' di intervallo tra i tempi di arrivo e il risultato è il seguente:
 RUNNER_ID ARRIVAL_TIME        TIME_DIFF
---------- ------------------- ----------------------------------------------------------------------------------
4038 2008-06-19 12:32:09 0'0"
3144 2008-06-19 12:44:10 12'1"
660 2008-06-19 12:51:40 7'30"
4450 2008-06-19 12:54:54 3'14"
2441 2008-06-19 12:58:28 3'34"
591 2008-06-19 13:02:55 4'27"
3825 2008-06-19 13:05:03 2'8"
3169 2008-06-19 13:11:21 6'18"
4036 2008-06-19 13:11:39 0'18"

La funzionalità di LEAD è analoga a LAG, solo che va nel verso opposto, ovvero considera i record precedenti a quello in cui viene valutata.

21 settembre 2008

Oracle DBA Italia

Tempo fa sul blog di Cristian Cudizio si parlava di qualche iniziativa per riunire tutti i DBA Oracle italiani in una specie di network.
Ho provato a pensare a qualche soluzione, e l'unica che mi è venuta in mente è utilizzare Facebook.
Ho deciso quindi di fare una prova: in pieno spirito Web 2.0, ho creato un gruppo Facebook per tutti i DBA Oracle in Italia.

Invito tutti gli interessati ad iscriversi, prima di tutto a Facebook, se non fossero già iscritti.

Per ora il gruppo è pubblico, quindi tutti possono iscriversi e utilizzare tutte le funzioni messe a disposizione da Facebook. Anche il contenuto delle pagine create dai membri rimane pubblico.

Le funzioni sono le seguenti:
  • Discussioni
  • Bacheca
  • Foto
  • Video
  • Link utili
  • Eventi

Ritengo che siano più che sufficienti per iniziare a interagire, scambiarsi informazioni che altrove (es. sui blog dei partecipanti) non trovano collocazione e, perché no, organizzare futuri incontri ed eventi.
Non dimentichiamo che anche le aziende potrebbero essere molto interessate a noi!

Forza quindi! Io per oggi ho contribuito con il logo. Sfogate la vostra voglia di comunicare!
Attendo commenti, contributi, modifiche, critiche, segnali di vita...

25 agosto 2008

Dischi, burro e marmellata

Sono quantomai sorpreso dell'apprendere, sull'ultimo numero dell'ottimo Oracle Magazine, che Oracle ha avviato pochi mesi fa un progetto open source per un nuovo potente filesystem.

La grande sorpresa consiste nel fatto che tale BTRFS dove, viene precisato, btr si pronuncia butter, ricalca fedelmente l'interessantissimo ZFS di Sun, disponibile già da tempo nella famiglia di sistemi operativi Solaris.

Avevo intenzione di fare un post solo su ZFS per le sue rivoluzionarie caratteristiche, ovviamente dal punto di vista Oracle, anche per il fatto che ci sto lavorando abbastanza intensamente e ne ho potuto apprezzare le ottime funzionalità.

A questo punto cominciamo da ZFS. La migliore definizione che mi viene in mente è che ZFS è un filesystem e volume manager integrati, con possibilità di snapshot stile LVM e possibilità di replica anche incrementale.

Un'importantissima caratteristica di ZFS è che è un filesystem copy-on-write (COW): ciò significa che tutte le modifiche ai dati vengono scritte su disco in nuove allocazioni, lasciando integra su disco la precedente versione dei dati. Ciò è da tenere ben presente per un DBA poichè, col passare del tempo, i dati tendono a venire sparpagliati sul disco, aumentando quindi il tempo d'accesso specie per le letture di dati in sequenza; si pensi ad esempio all'utilità del db_file_multiblock_read_count di Oracle, anche se è ormai quasi obsoleto.

ZFS è anche un volume manager, ovvero può prendere in gestione più dischi e comporre zpool più grandi, in cui gestisce automaticamente lo striping; oppure può garantire livelli di tolleranza ai guasti come il mirroring e il RAIDZ, una modalità che somiglia molto al RAID5 o addirittura RAID6 con due dischi di "parità".

Il COW diventa meno importante man mano che il numero dei dischi a disposizione aumenta, poiché lo striping riduce naturalmente il raggruppamento dei dati in blocchi contigui su disco.

ZFS è un filesystem transazionale, ovvero le modifiche ai dati o avvengono in blocco o non avvengono; ciò, unito al COW, consente di fare a meno del journal, caratteristica di punta di filesystem avanzati come ReiserFS o ext3.

Con ZFS si possono anche fare snapshot consistenti istantanei dei filesystem, direttamente accessibili sul filesystem stesso, quindi copiabili e salvabili. Ciò rende possibile un hot-backup con il comando 10g ALTER DATABASE BEGIN BACKUP, come potrebbe avvenire con storage molto più costosi.

Da un punto di vista amministrativo ZFS è troppo semplice per essere vero: consente in pochi e semplicissimi comandi di sintetizzare numerose operazioni di amministrazione del sistema. E non solo, offre un supporto nativo a iSCSI e NFS senza toccare alcun file di configurazione.

Torniamo a BTRFS: le caratteristiche sul sito Wiki sembrano proprio ricalcare quelle di ZFS, che è anch'esso open-source. Ma allora perché non integrarlo nel kernel Linux?
Il problema sta nella licenza, che non è GPL (e sembra che Sun abbia fatto apposta).

La mia sensazione però è che alla fine non se ne faccia un granché, visto che ZFS è già in produzione avanzata sotto Solaris, mentre BTRFS è all'inizio dello sviluppo.

Tornerò sul tema ZFS per dirvi degli interessanti documenti in rete sui database e dei risultati delle mie prove.

14 agosto 2008

Oracle 11g su OpenSuSE 11

Visto che uno dei due dischi del mio megaserver di casa ha deciso, bontà sua, di rimettersi a funzionare, sono riuscito a fare vari esperimenti di cui vi parlerò, tra cui la prova di installazione di Oracle 11g su OpenSuSE 11.

L'installazione è andata molto bene e senza alcun errore, quindi la piattaforma preferenziale per le mie prossime installazioni di prova 11g sarà sicuramente OpenSuSE 11.

L'unica variazione nella configurazione, rispetto a quella di default, è l'aggiunta delle componenti di sviluppo C/C++ durante l'installazione, e un package libstdc++-devel che ho aggiunto io per precauzione.

Il vantaggio maggiore di utilizzare una distribuzione Linux non supportata da Oracle è di avere un sistema operativo completamente aggiornato, e in modo gratuito. Per noi che smanettiamo spesso con questo genere di software sarebbe bello che tutto ciò che dobbiamo installare sia liberamente utilizzabile per fini di ricerca.

12 luglio 2008

ASM a linea di comando

Ogni tanto arriva qualche e-mail dai lettori di questo blog. La maggior parte delle domande riguarda ASM, il volume manager/filesystem di Oracle dedicato all'immagazzinamento dei dati del suo motore RDBMS.

Forse non tutti sanno che ASM è accessibile anche da linea di comando con asmcmd.

asmcmd è sostanzialmente un prompt dei comandi che si collega all'istanza ASM attiva sulla $ORACLE_HOME impostata nell'ambiente, con il relativo $ORACLE_SID impostato a +ASM o +ASM<n> nel caso di RAC.

Eseguiamo asmcmd:
oracle@host:~> asmcmd -p
ASMCMD [+] >

Il flag -p permette di mostrare il path tra parentesi quadre.
I comandi disponibili sono: cd, du, find, help, ls, lsct, lsdg, mkalias, mkdir, pwd, rm, rmalias.
Per ogni comando si può avere l'help con help <comando>.
All'avvio troviamo l'elenco dei diskgroup montati dall'istanza ASM e in seguito i database montati in notazione OMF:
ASMCMD [+] > ls
DG1/
ASMCMD [+] > ls
ASMCMD [+] > cd DG1
ASMCMD [+DG1] > ls
DBTEST/
ASMCMD [+DG1] >

All'interno delle directory DB troveremo le sottodirectory in notazione OMF come su filesystem ordinario:
ASMCMD [+DG1/DBTEST] > ls
ARCHIVELOG/
CONTROLFILE/
DATAFILE/
ONLINELOG/
PARAMETERFILE/
TEMPFILE/
spfileDBTEST.ora
ASMCMD [+DG1/DBTEST] >

Il comando du mostra l'utilizzo del diskgroup:
ASMCMD [+DG1/DBTEST] > du .  
Used_MB Mirror_used_MB
24529 24529

Il comando lsdg mostra tutti i diskgroup:
ASMCMD [+] > lsdg
State Type Rebal Unbal
Sector Block AU Total_MB
Free_MB Req_mir_free_MB Usable_file_MB
Offline_disks Name
MOUNTED EXTERN N N
512 4096 1048576 347430
322841 0 322841
0 DG1/


Contrariamente a un filesystem normale, esistono i comandi per creare e rimuovere alias mkalias e rmalias.
Gli alias in ASM sono file finti nel filesystem che puntano a file normali che risiedono sempre in ASM; possono essere visti come link simbolici. Ricordiamo infatti che ogni file in un volume ASM è un file OMF, ovvero è un file del database Oracle (controlfile, datafile, redo, e così via) con un nome scelto dal RDBMS, ma non un file ordinario. Per ogni file del db, quindi, esiste uno e un solo file OMF con il nome scelto da Oracle. Per avere file con nomi più leggibili o facili da ricordare si usano gli alias, a cui si può fare riferimento con i comandi DDL da prompt SQL al posto dei file OMF. Gli alias possono risiedere ovunque in ASM.

03 luglio 2008

Vantaggi del sampling dinamico

Ho recentemente letto un curioso aricolo di Tom Kyte su Oracle Magazine sul dynamic sampling e i suoi vantaggi rispetto alle statistiche usate dal cost-based optimizer (CBO) in alcuni casi particolari.

La prima parte dell'articolo parla di V$SQL ed è molto interessante. Qui mi occupo della seconda parte, ovvero della cardinalità nel piano di esecuzione a seconda che venga usato il CBO o il sampling dinamico.

Nell'articolo si fa l'esempio di una tabella in cui due colonne (X e Y) hanno 12 valori distinti distribuiti uniformemente tra le righe, per cui una query del tipo
SELECT * FROM T WHERE X=5;

restituisce 1/12 delle righe, mentre
SELECT * FROM T WHERE X=5 AND Y=6;

restituisce 1/144 delle righe (1/12 * 1/12).
O almeno, questa è la selettività che si ottiene dalle statistiche sulla tabella.

Ma che cosa accade se X=Y per ogni riga? Una query come
SELECT * FROM T WHERE X=5 AND Y=6;

ritornerebbe 0 righe, quindi la stima del CBO sarebbe errata (introducendo eventuali piani di esecuzione sballati).

Utilizzando il sampling dinamico:
select /*+ dynamic_sampling(t 3) */ * from t

si ottiene invece il costo corretto, molto più basso, e quindi anche il piano corretto.

Il sampling dinamico è regolabile a vari livelli, espressi dal numero che segue il nome della tabella nell'hint all'optimizer. Il valore minimo del sampling è 32 blocchi con n=1; per i livelli da 1 a 9 vale la formula 32*2^(l-1) blocchi dove l è il livello di sampling. Nel nostro caso di n=3 si ha un sampling di 4*32=128 blocchi, per cui quasi certamente l'optimizer ha letto l'intera tabella. Per questo motivo il costo finale è molto preciso. Se il livello è 0 non si ha sampling, se è 10 il sampling viene fatto su tutti i blocchi della tabella (quindi può risultare mooolto lento).

Il dynamic sampling è quindi una possibilità in più utilizzabile nei casi in cui il CBO ci fornisce piani di esecuzione subottimali.

OCA 10g solo con esame SQL

Dal 28 novembre 2008 verrà richiesto un esame SQL per ottenere la certificazione OCA 10g, contrariamente a quanto è successo fin'ora, da quando fu introdotta la certificazione di base 10g.

Vedo con piacere che il buon vecchio 1Z0-007 (SQL 9i) torna in auge.

23 giugno 2008

Analytics - n.1: NTILE

Con questo post inauguro la serie "Analytics", ovvero come utilizzare le potenti funzioni analitiche di Oracle 10g.

NTILE è una funzione che permette di ottenere il numero corrente di partizione dei dati, basandosi su un criterio di partizione determinato nella query.

Facciamo subito un esempio: ho una tabella che contiene i tempi di arrivo di una maratona con tanti partecipanti, diciamo 5000. Di questi, 1000 sono arrivati al traguardo, mentre gli altri si sono persi per strada.
La tabella contiene un ID univoco del corridore e il suo tempo di arrivo.

Supponiamo che io voglia dividere l'insieme degli "arrivati" in n gruppi omogenei, cioè aventi lo stesso numero di componenti.
Gli ID, anche se generati con una sequenza, sono distribuiti casualmente, dato che solo 1/5 dei corridori è arrivato alla fine; lo stesso vale per i tempi di arrivo.

La funzione NTILE ci permette di dividere il nostro set di dati in partizioni uguali, assegnando ad ogni riga il numero di partizione in cui si trova. Il criterio per trovarsi in una partizione o in un'altra è il tempo di arrivo in ordine crescente.

Dividiamoli ad esempio in 10 gruppi:
SQL> select * from marathon;

RUNNER_ID ARRIVAL_TIME
---------- -------------------
93 2008-06-19 13:11:39
3144 2008-06-19 12:44:10
4450 2008-06-19 12:54:54
453 2008-06-19 12:52:58
808 2008-06-19 12:48:00
4036 2008-06-19 13:11:39
4048 2008-06-19 13:06:48
660 2008-06-19 12:51:40
4091 2008-06-19 13:12:38
591 2008-06-19 13:02:55
[...]

Con la query seguente otteniamo la divisione in gruppi:
SQL> select runner_id, arrival_time, ntile(10) over (order by arrival_time) nt from marathon;

Come si può vedere, ci sono esattamente 100 corridori in ogni gruppo:
SQL> select nt, count(*) from
(select runner_id, arrival_time,
ntile(10) over (order by arrival_time) nt from marathon)
group by nt order by nt;

NT COUNT(*)
---------- ----------
1 100
2 100
3 100
4 100
5 100
6 100
7 100
8 100
9 100
10 100

10 rows selected.

Possiamo anche calcolare l'ntile su una partizione dei dati originari: immaginiamo per esempio che ai corridori siano state assegnate delle pettorine colorate di rosso se il loro id era tra 1 e 1000, verde se tra 1001 e 2000, blu se maggiore di 2000, divisi quindi per categoria.
Una query come la seguente divide in dieci sottogruppi ogni categoria, basandosi sempre sul tempo di arrivo:
SQL> select runner_id, arrival_time, color,
ntile(10) over (partition by color order by arrival_time) from marathon;