Struttura interna di un blocco dati Oracle

mercoledì 10 giugno 2009 alle 10:03 | Pubblicato su Installation and Configuration, Varie | 1 commento

Un po’ di tempo fa  (esattamente era il 20 maggio) Jonathan Lewis ha lanciato sul suo blog un quiz che chiedeva per una tabella (di cui riportava lo script di creazione) con una colonna e con il parametro pctfree=0 quante “row entries” si potevano creare in un blocco da 8k.

Il quiz non attirò molto la mia attenzione, ma la soluzione, forse per il fascino dell’occulto che si cela dietro l’esplorazione dei meccanismi interni di Oracle, mi ha incuriosito. Ho quindi deciso di fare alcune prove per capire meglio la problematica e l’argomento proposto da Jonathan Lewis.  Molti aspetti di ciò che dice Lewis in quei post (e nei commenti) mi sono diventati più chiari solo dopo un po’ di prove e di studi, anche perché l’argomento non è banale e il post di spiegazione è piuttosto sintetico.

Nella formulazione della domanda Lewis metteva in guardia sul fatto che la domanda era “insidiosa” con il P.S. “it’s a bit of a trick question.

Infatti il punto non era quante righe Oracle mette al massimo in un blocco (che vedremo essere 733 in una tablespace LMT) ma quante “row entries” possono essere create. Le row entries sono le componenti della row directory ovvero l’indice delle righe nella parte di “intestazione” del blocco.

Oracle Data Block Format (10g Concepts Manual)

Oracle Data Block Format (10g Concepts Manual)

La figura sopra riportata, presa direttamente dal manuale “Concepts” della versione 10gR2 è semplificata rispetto alla realtà in quanto è sufficente per descrivere i concetti spiegati nel manuale e per il normale utilizzo. In realtà subito dopo la figura si dice che i primi tre elementi (data block header, table directory, and row directory) chiamati “overhead” sommano tra gli 84 e i 107 byte cosa inesatta, in quanto solo la row directory può arrivare a 2*2015 byte (sempre per un blocco da 8k in una tablespace LMT).

Ho fatto varie ricerche sull’esatta struttura dell’header di un blocco dati Oracle ma tutte le ricerche mi portavano alle informazioni presenti ad esempio in questo sito. Informazioni presenti anche sul metalink ma che suppongo riferite a tablespace Dictionary Managed (DMT). Ora,io utilizzo oracle dalla 9iR2 e tutti i miei database hanno la tablespace SYSTEM Locally Managed (possibilità introdotta proprio con 9iR2, con la 9i, versione con cui è stata introdotta questa caratteristica, la system non poteva essere creata così), come da scritp generati da DBCA. Ancora nella 10gR2 il default per la system sarebbe Dictionary Managed. Come indicato sul manuale se la tablespace SYSTEM è LMT allora non si possono creare tablespace Dictionary Managed e quindi non avendo voglia di creare un nuovo database di test non ho fatto test su questo tipo di tablespace (fra l’altro deprecate).

Una descrizione precisa e dettagliata l’ho trovata in questo documento, dal quale si ricava che l’intestazione di un blocco dati oracle è così:

  1. 20 byte di “block header structure“, intestazione fissa uguale per tutti i blocchi
  2. 72 byte di “Transaction Fixed Header Structure” che si divide a sua volta in:
    1. 24 byte di sub-instestazione fissi
    2. 48 byte per due slot ITL (interested transaction list)
  3. 14 byte per la “Data Header Structure
  4. 4 byte per la “Table Directory Entry Structure

Vi è poi la Row Directory, lo spazio libero, i dati (il blocco viene riempito partendo dal basso) e gli ultimi 4 byte sono riservati alla “Tail check“, una specie di checksum utilizzato per controllare l’integrità del blocco.

Risulterebbero 110 byte sull’header e 4 byte di tailcheck.  Un commentatore però riporta pero’ sempre sul blog di Lewis la formula

BLOCK_SIZE = 122 + (2 + 9) * NUM_ROWS

Che risulta essere corretta, ma della quale non riuscivo a comprendere il termine “122”, a me mancavano 8 byte all’appello. Guardando il documento di Graham Thornton a pagina 14 e i dump dei blocchi (che dalla versione 10g contiene anche il dump in esadecimale) risulta che la parte “Data Header Structure” comincia al centounesimo byte (indirizzo 100) e che i byte fra il novantreesimo e il il centesimo (92-99) sono a zero, quindi o inutilizzati o riservati per usi particolari. Non ho trovato una spiegazione logica per questo buco, fatto sta che contando questi 8 byte si arriva a un minimo di 122 byte utilizzati in un blocco dati per informazioni di struttura fissi.

Dunque, il numero massimo di record che Oracle mette in un blocco da 8k (in una LMT) è 733 come ho detto sopra, secondo la formula sopra scritta però:

MAX_ROWS= (BLOCK_SIZE – 122) /(2+1+1+1+6)

dove in (2+1+1+1+6 sono rispettivamente l’occupazione della row entry, il flag byte, il lock byte, il count column e il restricted rowid (occupazione minima in caso di una migrated row come spiegato da Lewis qui).

Interessanti informazioni sul contenuto e il formato di un blocco dati Oracle possono essere ricavate dalla presentazione di Rich Niemec intitolata “Tuning Oracle at the Block Level; Beginners, Go Away!” scaricabile dal sito di TUSC.

In particolare, tornando alla spiegazione di Lewis sulle Row entries il punto che mi ha colpito è stato il fatto che c’è un limite invalicabile di 4096 row entries che in oracle 10gR2 con il testcase di Lewis provoca il seguente errore:

ORA-08007: Further changes to this block by this transaction not allowed

Il punto è che la struttura della ITL è così fatta:

  1. 8 bytes di XID
  2. 8 bytes di UBA
  3. 2 byte di Flag+Lock
  4. 6 byte di  scn/fsc (la cui struttura non mi è completamente chiara)

Una descrizione di questi elementi si trova nel documento di Niemec nelle pagine 23 e 24.


Itl           Xid                  Uba         Flag  Lck        Scn/Fsc
0x01   0x0006.015.000021ed  0x00803290.0f44.5b  ----  4092  fsc 0x0007.00000000
0x02   0x0000.000.00000000  0x00000000.0000.00  ----    0  fsc 0x0000.00000000

La parte flag occupa 4 bit e alla parte lck, che indica il numero di righe “loccate” nel blocco rimangono 12 bit, 2^12=4096 da qui il limite imposto da Oracle (fra l’altro aggiunto dopo la segnalazione di corruzioni che probabilmente sconfinavano nella parte flag).

Esplorare e comprendere la struttura interna di un blocco dati oracle non so quanto sia utile, sicuramente richiede tempo ed energie. Aiuta a capire casi strani come quello riportato da Jonathan  Lewis e alcuni limiti. E’ una cosa che inspiegabilmente mi attira molto e che mi ha portata a investire un po’ di tempo. Buona parte dei risultati delle mie investigazioni sono state riassunte in questo post. Non ho potuto chiaramente descrivere ogni dettaglio, forse lo farò in post successivi.

About these ads

1 commento »

RSS feed dei commenti a questo articolo. TrackBack URI

  1. Concordo con te sulla poca utilità dell’investigazione di alcune internals di Oracle, soprattutto perché c’è una gran quantità di altri argomenti da esplorare, molto più impattanti nella realtà lavorativa quotidiana.
    Tuttavia la tentazione è sempre dietro l’angolo e infatti sono io il Roberto che ha proposto la formula:

    BLOCK_SIZE = 122 + (2 + 9) * NUM_ROWS

    E’ conseguenza di una serie di miei appunti, da me organizzati qualche anno fa, quando mi sottoposero l’annoso problema di pre-stimare gli spazi di occupazione di tabelle.
    Partendo dal solito sito http://www.ixora.com.au di Steve Adams (ahimè sempre più datato) e insieme a varie altre informazioni frammentarie, confuse e spesso non corrette di altri, ho individuato le varie parti costituenti la struttura del blocco.
    Il 122 della formula relativo alle LMTs, ai tempi che furono, sinceramente non sono mai riuscito a trovare un documento che ne parlasse e l’avevo ottenuto dal dump del blocco, ad ogni modo vale quanto detto all’inizio e, come tu mi confermi, hanno aggiunto un po’ di bytes per altri usi (o in previsione di altri usi).
    Comunque il mio intervento ha lo scopo di suggerirti di correggere la formula:
    MAX_ROWS= BLOCK_SIZE /( 122 + (2+1+1+1+6))
    in:
    MAX_ROWS = (BLOCK_SIZE-122)/(2+1+1+1+6) = 733.63… per BLOCK_SIZE=8192


Rispondi

Inserisci i tuoi dati qui sotto o clicca su un'icona per effettuare l'accesso:

Logo WordPress.com

Stai commentando usando il tuo account WordPress.com. Chiudi sessione / Modifica )

Foto Twitter

Stai commentando usando il tuo account Twitter. Chiudi sessione / Modifica )

Foto di Facebook

Stai commentando usando il tuo account Facebook. Chiudi sessione / Modifica )

Google+ photo

Stai commentando usando il tuo account Google+. Chiudi sessione / Modifica )

Connessione a %s...

Crea un sito o un blog gratuitamente presso WordPress.com. | The Pool Theme.
Entries e commenti feeds.

Iscriviti

Ricevi al tuo indirizzo email tutti i nuovi post del sito.

Unisciti agli altri 71 follower

%d blogger cliccano Mi Piace per questo: