Database Systems
Chapter 9: Technology of a database server
Hash-based structures
• Ensure an efficient associative access to data, based on the
value of a key field, composed of an arbitrary number of
attributes of a given table
• A hash-based structure has B blocks (often adjacent)
• The access method makes use of a hash algorithm, which, once
applied to the key, returns a value between zero and B-1
• This value is interpreted as the position of the block in the file,
and used both for reading and writing tuples to the file
• The most efficient technique for queries with equality
predicates, but inefficient for queries with interval predicates
McGraw-Hill 1999
Database Systems
Chapter 9: Technology of a database server
Features of hash-based structures
• Primitive interface: hash(fileid,Key):Blockid
•
The implementation consists of two parts
– folding, transforms the key values so that they become
positive integer values, uniformly distributed over a large
range
– hashing, transforms the positive binary number into a
number between 0 and B - 1
• This technique works better if the file is made larger than
necessary. Let:
– T the number of tuples expected for the file
– F the average number of tuples stored in each page,
then a good choice for B is T/(0.8 x F), using only 80% of the
available space
McGraw-Hill 1999
Esercizio Hashing
Si consideri una base di dati gestita tramite hashing,
il cui campo chiave contenga i seguenti nominativi:
Green, Lovano, Osby, Peterson, Pullen Scofield,
Allen, Haden, Sheep, Harris, MacCann, Mann,
Brown, Hutcherson, Newmann, Ponty, Cobbham,
Coleman, Mingus, Lloyd, Tyner, Fortune, Coltrane.
1) Proporre un algoritmo di hashing con B=8.
Una semplice funzione di hashing per i nomi dati è:
• Per ogni carattere del nome, considerare il
corrispondente numero in ordine alfabetico (a =
1, b = 2 …)
• Sommare tutti i numeri ottenuti e fare il
“modulo B” della divisione
In questo caso otterremo per ogni nome un
numero compreso tra 0 e B-1.
Soluzione:
Hash(Green) = (7+18+5+5+14) mod 8=1
Hash(Lovano) = (12+15+22+1+13+15) mod 8=6
Hash(Osby) = (15+19+2+25) mod 8=5
Hash(Peterson)=
(16+5++20+5+18+19+15+13) mod 8=7
...
• T = 100 e F = 4
1) Supponendo di utilizzare solo 80% dello spazio
disponibile, che valore assume la variabile B (#
blocchi)?
T = 100 # tuple nel file
F = 4 (numero medio di tuple nella stessa pagina)
1) Supponendo di utilizzare solo 80% dello spazio
disponibile, che valore assume la variabile B (#
blocchi)?
B = T/(u*F) -> B = 100 / (0,8 * 4) = 31,25 ≈ 31
Progetto di strutture hash
Si vuole memorizzare una tabella di 16000 record, della
dimensione fissa di 1KByte ciascuno e con chiave primaria
rappresentata da un intero ad autoincremento, tramite una
struttura ad accesso hash-based. In particolare, occorre
scegliere se impiegare pagine fisiche della dimensione di 4
oppure di 16 Kbyte. Si scelga un opportuno coefficiente di
riempimento nei due casi, si proponga una possibile
funzione di hash, e si indichi il numero di pagine necessarie
a contenere la tabella nei due casi, in base al progetto.
Scegliamo per semplicità un fattore di riempimento “lasco”, del 50%, e
una semplice funzione di modulo sulla chiave primaria come funzione di
hash, in entrambi i casi, assumendo distribuzione uniforme sui valori di
chiave (dopo molti insert e delete).
• In caso di distribuzione omogenea, ogni blocco contiene 4K x 50% / 1K
= 2 tuple, quindi occorrono 16000/2 = 8000 pagine, usando una
funzione h(t) = key(t) mod 8000, dove key() indica la chiave primaria
• Con pagine da 16K, invece, ogni blocco contiene 16K x 50% / 1K = 8
tuple, quindi occorrono 16000/8 = 2000 pagine, usando una funzione
h(t) = key(t) mod 2000,
Ovviamente diversi livelli di riempimento desiderati comportano un
diverso numero di pagine e una diversa funzione di hash.In particolare,
volendo differenziare i fattori di riempimento, ha più senso aumentarlo per
la variante B. Con un fattore di riempimento dell’80% avremmo 16000 /
(16K x 80%) x 1K = 1250 pagine, con quindi h(t) = key(t) mod 1250
Database Systems
Chapter 9: Technology of a database server
Tree structures
• The most frequently used in relational DBMSs
• Gives associative access (based on a value of a key, consisting
of one or more attributes) without constraints on the physical
location of the tuples
• Note: the primary key of the relational model and the
key of tree structures are different concepts
McGraw-Hill 1999
Database Systems
Chapter 9: Technology of a database server
Tree structure organization
• Each tree has:
– a root node
– a number of intermediate nodes
– a number of leaf nodes
• The links between the nodes are established by pointers
• Each node coincides with a page or block at the file system and
buffer manager levels. In general, each node has a large
number of descendants (fan out), and therefore the majority of
pages are leaf nodes
• In a balanced tree, the lengths of the paths from the root node to
the leaf nodes are all equal. In this case, the access times to the
information contained in the tree are almost constant (and
optimal)
McGraw-Hill 1999
Database Systems
Chapter 9: Technology of a database server
Information contained in a node (page) of a B+ tree
P0
K1
Sub-tree that contains
keys K < K1
P1
. . . . .
Kj
Pj
. . . . .
Sub-tree that contains
keys Kj ≤ K< Kj+1
KF
PF
Sub-tree that contains
keys K ≥ KF
© McGraw-Hill 1999
Database Systems
Chapter 9: Technology of a database server
Split and merge operations on a B+ tree structure
initial situation
k1
k6
k1
k2
k4
k1
k3
k6
k5
a. insert k3: split
k1
k2
k3
k4
k5
b. delete k2: merge
k1
k6
k1
k3
k4
k5
© McGraw-Hill 1999
Database Systems
Chapter 9: Technology of a database server
Difference between B and B+ trees
• B+ trees:
– The leaf nodes are linked by a chain, which connects them
in the order imposed by the key
– Support interval queries efficiently
– Mostly used by relational DBMSs
• B trees:
– There is no sequential connection of leaf nodes
– Intermediate nodes use two pointers for each key value Ki
• one points directly to the block that contains the tuple
corresponding to Ki
• the other points to a sub-tree with keys greater than Ki
and less than Ki+1
McGraw-Hill 1999
Database Systems
Chapter 9: Technology of a database server
Example of B+ tree
Root node
Peter
First level
Mavis
Rick
Second level
Babs
David
Mavis
Peter
Rick
Tracy
Pointers to data (arbitrarily organized)
© McGraw-Hill 1999
Database Systems
Chapter 9: Technology of a database server
Example of a B tree
k1
k2
k3
t(k2)
k4
k6
k 10
k5
t(k3) t(k4) t(k5) t(k1)
k7
t(k6) t(k10)
k8
t(k7)
k9
t(k8) t(k9)
© McGraw-Hill 1999
Esercizio 1
Costruire un B-Tree con i seguenti valori come chiave:
12, 18, 21, 28, 32, 47
12
http://slady.net/java/bt/view.php?w=800&h=600
12, 18, 21, 28, 32, 47
12
18
12, 18, 21, 28, 32, 47
18
12
-
21
12, 18, 21, 28, 32, 47
18
12
-
21
28
12, 18, 21, 28, 32, 47
18
12
-
21
28
-
32
-
12, 18, 21, 28, 32, 47
18
12
-
21
28
-
32
47
1) Inserimento 13
18
12
13
21
28
-
32
47
1) Inserimento 8
18
12
13
28
21
32
47
18
12
8
28
13
21
32
47
1) Cancella 47
18
12
28
13
8
32
21
47
18
12
8
28
13
21
32
1) Cancella 21
18
12
28
13
8
12
8
32
21
13
18
28
32
1) Inserimento 30
12
8
18
28
13
12
8
13
18
32
28
30
32
1) Inserimento 29
12
8
13
12
18
30
18
13
8
28
28
32
29
30
32
Esercizio 2
Esercizio
Si realizzi un albero B+ con F=3 (numero di valori di
chiave presenti in ogni nodo) per rappresentare i
valori: limone, cipresso, ciliegio, tasso, melo, agrifoglio,
acero, fico, pittosphoro, cotoneaster, noce, oleandro,
agave, euforbia, dracena.
Si mostri poi come potrebbe avvenire un inserimento
che richieda lo split di un nodo. Si costruisca anche un
albero B (ottimizzato) per gli stessi valori.
Requisito: tutti i nodi (tranne la radice) siano riempiti
per almeno la metà (almeno la metà dei puntatori di
ogni nodo siano diversi da NULL)
9
Il primo pensiero è di riempire l’albero in modo
sequenziale. Però un nodo intermedio risulta
sparso (ha 3 NULL)
OLE
CIL
DRA
ACE AGA AGR
CIL
LIM
DRA EUF
CIP
COT
FIC
LIM
OLE
PIT
TAS
MEL NOC
ATTENZIONE! I puntatori puntano ai NODI, non
agli specifici valori!!. Quelli tratteggiati puntano
alle tuple “fisiche”, e sono solo sulle foglie (B+).
I puntatori a NULL non sono rappresentati
10
I due nodi intermedi possono essere più
“bilanciati”, avendo rispettivamente due e tre
puntatori diversi da NULL
LIM
CIL
DRA
ACE AGA AGR
CIL
OLE
DRA EUF
CIP
COT
FIC
LIM
OLE
PIT
TAS
MEL NOC
Ora tutti i nodi tranne la radice sono “non sparsi”
11
Consideriamo ora l’inserimento del valore “betulla”:
dovrebbe posizionarsi nel primo nodo foglia, che però è
saturo e si “splitta” in due nodi (entrambi non sparsi).
ACE AGA AGR
CIL
DRA
CIL
CIP
COT
AGR
CIL
DRA EUF
FIC
DRA
ACE AGA
AGR BET
CIL
CIP
COT
DRA EUF
FIC
12
Costruiamo infine il B-tree, notando 15 = 3*(4+1)
Anche i nodi non-foglia hanno puntatori alle tuple
fisiche, poiché nessun valore è replicato.
CIL
EUF
NOC
ACE AGA AGR
OLE
FIC
CIP
LIM
PIT
TAS
MEL
COT DRA
I nodi foglia non hanno puntatori ad altri nodi
dell’indice, perché sarebbero tutti a NULL, ma solo alle
tuple (un dettaglio non menzionato nel libro).
13
Esercizio 3
Tema esame 16/04/1999
Si consideri la seguente sequenza di valori di chiave: Honda, Kawasaki, Piaggio,
Suzuki, Yamaha, Aprilia, Ducati, Cagiva, Laverda, Buell, Husqvarna, Ktm, Swm, Mv
Agusta, Moto Guzzi, Zundapp, Bmw, Harley Davidson
Costruire una struttura B+-tree e una B-tree, entrambe con F=3, contenenti tutti i
precedenti valori chiave.
HON, KAW, PIA, SUZ, YAM, APR, DUC, CAG, LAV, BUE, HUS, KTM, SWM,
MVA, MTG, ZUN, BMW, HAR
APR,BMW, BUE, CAG, DUC, HAR, HON, HUS, KAW, KTM, LAV, MTG, MVA,
PIA, SUZ, SWM, YAM, ZUN
B+-tree
KTM
CAG
APR
BMW
HON
-
-
-
MVA
SWM
MVA
PIA
-
BUE
HON
CAG
DUC
HUS
KAW
SUZ
SWM
HAR
KTM
LAV
MTG
YAM
ZUN
B-tree
MTG
CAG
APR
BMW
HUS
-
-
SWM
-
KAV
BUE
-
KTM
LAV
YAM
DUC
HAR
HOR
MVA
PIA
SUZ
ZUN
Green, Lovano, Osby, Peterson, Pullen Scofield, Allen, Haden,
Sheep, Harris, MacCann, Mann, Brown, Hutcherson, Newmann,
Ponty, Cobbham, Coleman, Mingus, Lloyd, Tyner, Fortune,
Coltrane.
1) Descrivere una struttura ad albero B+ bilanciato, con F=2, che contenga i dati
citati
2) Introdurre un dato che provochi lo split di un nodo al livello foglia, e mostrare
cosa accade al livello foglia e al livello superiore.
3) Introdurre un dato che provochi il merge di un nodo al livello foglia, e mostrare
cosa accade al livello foglia e al livello superiore.
4) Indicare una sequenza di inserimenti che provochi, ricorsivamente, lo split della
radice e l’allungamento dell’albero.
5) Descrivere una struttura ad albero B, con F=3, che contenga i dati citati.
1) Descrivere una struttura ad albero B+ bilanciato, con F=2, che contenga i dati citati
1) Introdurre un dato che provochi lo split di un nodo al livello foglia, e mostrare cosa
accade al livello foglia e al livello superiore.
L’introduzione del valore “Brooke”, causa uno split a livello foglia, come illustrato nella
seguente figura.
Introdurre un dato che provochi il merge di un nodo al livello foglia, e mostrare cosa accade
al livello foglia e al livello superiore.
La cancellazione del valore “Mann” causa un merge al livello foglia.
Indicare una sequenza di inserimenti che provochi, ricorsivamente, lo split della radice e
l’allungamento dell’albero.
Descrivere una struttura ad albero B, con F=3, che contenga i dati citati.
Database Systems
Chapter 9: Technology of a database server
Join methods
• Joins are the most costly operation for a DBMS
• There are various methods for join evaluation, among them:
– nested-loop, merge-scan and hashed
• The three techniques are based on the combined use of
scanning, hashing, and ordering
McGraw-Hill 1999
Database Systems
Chapter 9: Technology of a database server
Join technique with nested-loop
Internal table
External table
JA
----------------
external
scan
a
JA
a
---------------
a
---------------
a
---------------
internal scan
or indexed
access
© McGraw-Hill 1999
Database Systems
Chapter 9: Technology of a database server
Join technique with merge scan
Left table
Right table
A
-------------------------------
a
b
b
c
c
e
f
h
left
scan
right
scan
A
a
a
b
c
e
e
g
h
---------------
© McGraw-Hill 1999
Database Systems
Chapter 9: Technology of a database server
Join technique with hashing
a
hash(a)
Left table
Right table
A
hash(a)
A
d
e
a
c
J
j
j
J
J
e
m
a
a
j
z
© McGraw-Hill 1999
Esercizio 2
●
Elencare le condizioni (dimensioni delle tabelle,
presenza di indici o di organizzazioni sequenziali o
a hash) che rendono più o meno conveniente la
realizzazione di join con i metodi nested loop, merge
scan e hash-based; per alcune di queste condizioni,
proporre delle formule di costo che tengano conto
essenzialmente del numero di operazioni di
ingresso/uscita come funzione dei costi medi delle
operazioni di accesso coinvolte (scansioni,
ordinamenti, accessi via indici).
Dimensione della tabella
●
se siamo in una situazione in cui una delle tabelle è
molto grande rispetto all’altra, può essere una buona
soluzione l’utilizzo di un join con il metodo nested
loop, usando la tabella maggiore come esterna e
quella più piccola come interna. Se le tabelle hanno
la stessa dimensione il join con il metodo nested
loop è conveniente in presenza di indici o hashing su
una delle tabelle. Negli altri casi è meglio scegliere
un merge-scan.
Hashing
●
la presenza di una funzione di hash può suggerire
l’utilizzo di un join hash-based o nested loop. La
scelta dipende dalla dimensione di una tabella e dal
numero di partizioni prodotte dalla funzione di hash.
Organizzazione sequenziale
●
naturalmente, se le due tabelle hanno
un’organizzazione sequenziale sugli attributi del
join, il merge-scan è la scelta migliore. Se solo una
tabella ha un’organizzazione sequenziale il merge
scan può essere ancora una buona scelta se non ci
sono particolari condizioni (indici o hashing).
Indici
●
La presenza di indici in generale suggerisce un
nested loop. Comunque, se la tabella con gli indici è
molto piccola, o se l’indice è sparso, può essere
meglio non utilizzare l’indice e fare uno scan
completo.
●
Il costo di un join di tipo nested loop senza indici
può essere espresso come:
CNL=scan(T1) + T1 (scan (T2))
●
●
Dove T1 e T2 sono il numero di tuple delle due
tabelle, e scan(T) è il costo medio di un’operazione
di scansione.
Se c’è un indice sulla tabella 2:
CNL=scan(T1) + T1 (index (T2))
●
Un merge scan di un’organizzazione non
sequenziale ha un costo:
CMS= order(T1) + order(T2) + scan(T1) + scan(T2)
Esercizio 3
select C, L from R1, R2, R3 where R1.C = R2.D and R2.F = R3.G and R1.B = R3.L and R3.H > 10 Mostrare un possibile piano di esecuzione (in termini di operatori di algebra relazionale e loro realizzazioni; prestate attenzione anche alla DISTINCT, in quanto le relazioni degli operatori non producono necessariamente insiemi, ma liste di tuple), giustificando brevemente le scelte più significative, con riferimento alle seguenti informazioni sulla base di dati: –
la relazione R1(ABC) ha 100.000 tuple, una struttura heap e un indice secondario su C; –
la relazione R2(DEF) ha 30.000 tuple, una struttura heap e un indice secondario sulla chiave D; –
la relazione R3(GHL) ha 10.000 tuple, una struttura heap e un indice secondario sulla chiave G.
Un possibile piano di esecuzione per l’interrogazione in oggetto
è basato essenzialmente sulla struttura fisica delle relazioni e
sulla loro cardinalità.
La parte dell’interrogazione da analizzare è quindi quella della
condizione, che viene riportata di seguito per comodità.
where R1.C = R2.D and R2.F = R3.G and R1.B = R3.L and
R3.H > 10
Si effettuano i seguenti passaggi:
●
●
●
MERGE SCAN sulle relazioni R1 ed R2, in quanto R1.C e
R2.D hanno un indice secondario.
NESTED LOOP sulle relazioni R2 e R3, ponendo R2.F =
R3.G, mantenendo R2 esterno e R3 interno.
Selezione, tramite una scansione, degli attributi R1.B = R3.L e
R3.H > 10
Esercizio 4
select distinct A, L from T1, T2, T3 where T1.C = T2.D and T2.E = T3.F and T1.B = 3 Mostrare un possibile piano di esecuzione (in termini di operatori dell’algebra relazionale e loro realizzazioni), giustificando brevemente le scelte più significative, con riferimento alle seguenti informazioni sulla base dati: ●
●
●
la relazione T1(ABC) ha 800.000 tuple e 100.000 valori diversi per l’attributo B, distribuiti uniformemente; ha una struttura heap e un indice secondario sulla chiave A •la relazione T2(DE) ha 500.000 tuple; è definito un vincolo di riferimento fra l’attributo E e la chiave F della relazione T3: ha una struttura heap e un indice secondario sulla chiave D la relazione T3(FL) ha 1.000 tuple e ha una struttura hash sulla chiave F.
Un possibile piano di esecuzione per l’interrogazione in oggetto è basato essenzialmente sulla struttura fisica delle relazioni e sulla loro cardinalità. La parte dell’interrogazione da analizzare è quindi quella della condizione, che viene riportata di seguito per comodità where T1.C = T2.D and T2.E = T3.F and T1.B = 3 Si effettuano i seguenti passaggi ●
●
●
Selezione, tramite una scansione, dell’attributo T1.B = 3. Questa soluzione ha un costo molto elevato, 800.000 accessi in memoria, ma permette di estrarre un numero molto basso di record, in quanto possiede 100.000 valori diversi e mediamente ci saranno 8 record per ogni occorrenza. JOIN di tipo nested loop sulle relazioni T1 e T2, ponendo T1.C = T2.D, tenendo il risultato del passo precedente come tabella interna e T2 come esterna. JOIN di tipo nested loop usando l’indice hash su T3.F
Esercizio 5
Calcolare il fattore di blocco e il numero di blocchi
occupati da una relazione con T = 1000000 di
tuple di lunghezza fissa pari a L = 200 byte in un
sistema con blocchi di dimensione pari a B = 2 kb.
Dimensioni tabella (DT):
DT = T L
DT = 1000000 200 byte = 200000000 byte = 190,73 MiB
Numero blocchi:
NB = DT/B
NB = 200000000/2048 = 97656.25 blocchi
Fattore di blocco:
FB = B/L
FB = (2Kb)2048/200(byte) = 10.24 record / blocco
Esercizio 6
Una tabella Pubblicazioni ha chiave primaria CodPubbl
e come chiave candidata l'ISBN. La tabella contiene
1.000.000 di tuple ed è memorizzata tramite
un'organizzazione fisica hash che utilizza l'attributo
CodPubbl come chiave e presenta un livello di
riempimento dell'80% (si hanno in media 10 tuple per
blocco, quindi il numero di accessi medi è pari a 1.11).
PRIMO QUESITO
a) Trascurando l'impatto dei meccanismi di caching e
quantificando solamente il numero di operazioni di lettura su
disco, valutare il costo di esecuzione della seguente query:
select * from Pubblicazioni
where ISBN = '001122345'
and ( CodPubbl = 'ABC123' or CodPubbl = 'DEF456' )
Soluzione primo quesito
Disponendo di una struttura basata su hash
per un attributo e nulla per l'altro, il planner
decide di accedere innanzitutto tramite la
parte "or".
Questo comporta due tentativi di trovare la
tupla (perché due sono i valori di
confronto), il che comporta 2,22 accessi in
media.
SECONDO QUESITO
b) È inoltre presente una struttura ad albero come indice
secondario sull'attributo ISBN, con fan out medio pari a 100.
Trascurando anche in questo caso il contributo dei meccanismi di
caching, valutare il numero medio di letture necessario per
eseguire la query precedente, motivando la risposta.
select * from Pubblicazioni
where ISBN = '001122345'
and ( CodPubbl = 'ABC123' or CodPubbl = 'DEF456' )
Soluzione
L'indice secondario ad albero sull'attributo ISBN
comporta non meno di 3 accessi al disco, in media
(se è ben bilanciato). Infatti log100106 = 3.
Quindi non conviene usarle tale indice, ma seguire
comunque la strategia precedente.
Esercizio 7
Si realizzi un albero B+ con F=3 (numero di valori di
chiave presenti in ogni nodo) per rappresentare i valori:
da 1 a 15.
Si mostri poi come potrebbe avvenire un inserimento che
richieda lo split di un nodo. Si costruisca anche un albero
B (ottimizzato) per gli stessi valori.
Requisito: tutti i nodi (tranne la radice) siano riempiti per
almeno la metà (almeno la metà dei puntatori di ogni
nodo siano diversi da NULL)
Esercizio 5
Si deve dimensionare un archivio gestito con tecnica
hashing relativo a 1.500.000 record ciascuno di
lunghezza 150byte. Sia 8MByte la dimensione di un
blocco fisico, e si supponga che lo spazio utile
all’interno del blocco per la memorizzazione dei dati sia
l’80%. Indicare la dimensione dell’archivio che consenta
una gestione efficiente degli accessi.
80% di occupazione delle pagine => dimensione del
File = 1.25 volte la dimensione dei dati
Soluzione
1.500.000*150*(1/0.8)=281 MByte
• 281M/8M=31MBlocchi
•
Esercizio 6
●
●
●
Un indice hash è una struttura di accesso secondaria che organizza un certo numero di
blocchi mediante una funzione hash calcolata sulla chiave dell'indice. Come negli indici
secondari costruiti in forma di alberi, nei blocchi si conservano solo i valori delle chiavi,
assieme ai puntatori fisici ai blocchi in cui (nella struttura primaria) sono conservate le
tuple (e l'organizzazione primaria può essere sequenziale, hash-based o ad albero). Si
supponga di avere una tabella
Studente(Matricola,Nome,Cognome,DataNascita,CorsoDiLaurea), di 200K studenti, con
un'organizzazione primaria ad albero B+ sull'attributo Matricola, che occupa 10K blocchi
e ha un fanout massimo pari a 100; è poi disponibile un indice secondario hash
sull'attributo Cognome, che occupa 2K blocchi. Stimare il costo, in termini di numero di
accessi a blocchi, per l'esecuzione delle seguenti query SQL:
(1) select * from Studente where Matricola = '623883'
(2) select * from Studente
where Cognome in ('Braga','Campi','Comai','Paraboschi') and Matricola > '575478'
(3) select * from Studente where Cognome < 'B'
Per semplicità, si trascurino le collisioni, l’effetto della cache e le differenze tra i vari tipi
di accesso a disco (random o sequenziali).
Facoltativo: cosa si può dire della query
(4) select * from Studente S1, Studente S2
where (S1.Nome,S1.Cognome)=(S2.Nome,S2.Cognome)
and S1.Matricola>S2.Matricola
Ogni nodo/blocco interno all’albero contiene fino a 99 matricole e 100
puntatori fisici, e in ogni nodo/blocco foglia dell'albero ci sono
invece 20 studenti (le intere tuple) e 1 solo puntatore fisico (al
blocco successivo nella “catena delle foglie”).
Immaginando molto densa la struttura ad albero (nodi
sostanzialmente tutti pieni e albero perfettamente bilanciato), oltre
alla radice abbiamo un livello intermedio di 100 blocchi interni e
10K blocchi “foglia”, il che corrisponde alla dimensione indicata
della struttura. L’albero avrà quindi una profondità pari a 3.
Nei blocchi dell'indice hash (la cui chiave di accesso non è una chiave
primaria) ci sono puntatori fisici ai blocchi che contengono i dati di
circa (in media) 200K / 2K = 100 puntatori fisici, corrispondenti a
meno di 100 valori di chiave (sarebbero esattamente 100 se non
esistessero studenti con lo stesso cognome, in realtà se na
avranno in media significativamente meno). I blocchi avranno un
fattore di riempimento che dipende dal rapporto di dimensione tra
un cognome e un puntatore fisico, che i dati non permettono di
stimare.
Il costo è quindi
(query 1). 3 accessi tramite albero (2 blocchi interni e una foglia), e
ho subito l’intera tupla cercata.
(query 2). Se uso l’indice sul Cognome pago 4+4 = 8 acessi, avendo
4 condizini in OR e 4 puntatori ai blocchi fisici da seguire. È
ragionevole ritenere che la condizione sulla matricola sia
inservibile (non riteniamo probabile che esistano molto meno di
8x20 = 160 studenti con matricola superiore a 575478).
(query 3). Siccome in base alla traccia non possiamo garantire che la
funzione di hash dia valori correlati all’ordinamento alfabetico,
nessuna delle due strutture di accesso ci aiuta, e dobbiamo
scandire l’intera tabella (10K accessi)
Facoltativo:
(query 4). Si tratta di fare un self-join sul Cognome e verificare le altre
due condizioni. Possiamo pensare di applicare un hash join usando
la struttura secondaria, osservando che tutti gli studenti “puntati”
dia puntatori di ogni bucket devono essere confrontati solo con
qualli che, nello stesso bucket, sono associati alla stessa matricola.
Il numero di accessi dipende quindi direttamente dalla frequenza
delle omonimie, e non può essere efficacemente stimato senza
informazioni accurate sulla distribuzione dei valori. Una stima
molto grossolana si può avere immaginando di confrontare
comunque tutti gli studenti con tutti gli altri studenti all’interno
dello stesso bucket, e darebbe 2K x ( 1 (lettura indice) + 100x99 )
=~ 2K ( 10 K ) = 20M. Una strategia nested loop che usasse
l’albero, invece, avrebbe un costo pari a 10K + 260K x (2 + 10K / 2
) = 500M (dove per ogni matricola si cercano gli omonimi solo tra
quelli con matricola superiore, scandendo in media solo mezza
tabella), decisamente peggiore.
Esercizio
Su una tabella R(A,B,C,D,E) con 10.000.000 di tuple, sono
definiti 3 indici: Idx1(A,C,D), Idx2(B,C,E) e Idx3(B,D,A), in cui il
valore di chiave di ogni indice è composto in ordine dagli
attributi indicati da sinistra a destra. Gli attributi A, B, C, D, E
presentano una distribuzione uniforme di valori, con il
seguente numero di valori distinti: val(A)=1000, val(B)=100,
val(C)=1000, val(D)=2000, val(E)=10.
| R | = 10.000.000 Idx1(A,C,D), Idx2(B,C,E), Idx3(B,D,A)
val(A)=1000, val(B)=100, val(C)=1000, val(D)=2000, val(E)=10
Indicare quale indice converrà usare per eseguire la
seguente query SQL (argomentando molto brevemente la
risposta):
select * from R
where B=k1 and C=k2 and D=k3 and E=k4
Soluzione
Dovendo valutare le quattro condizioni in and
che offre, per gli attributi citati nei predicati, il maggior indice di
selettività, scartando subito Idx1 perché inizia con un attributo
che non è utilizzato dai predicati presenti nella condizione:
Idx2: 10.000.000/(100x1000x10) = 10 tuple in media da scandire
Idx3: 10.000.000/(100x2000) = 50 tuple in media da scandire
Quindi conviene usare
Idx2
Si supponga di avere una tabella
A tale of two Joins
Studente (Matricola,Nome,Cognome,DataNascita,CorsoDiLaurea)
che descrive 150K studenti su 10K blocchi, con un'organizzazione primaria hash, calcolata
sull'attributo Matricola. Si ha poi una tabella
Esame (Matricola,CodCorso,Data,Voto)
con 2M tuple su 8K blocchi con struttura sequenziale entry-sequenced e un indice secondario
sull'attributo Matricola, di fanout pari a 200. Si deve eseguire la query di join
select *
form Studente S join Esame E on S.Matricola = E.Matricola
Stimare il costo, in termini di accessi a disco, per eseguire uno hash join in cui la tabella
Esame viene ri-organizzata secondo la stessa funzione hash utilizza per la tabella Studente.
Si confronti questo costo con il costo richiesto dall'uso di un join nested loop con la
tabella Studente come tabella esterna. Si trascurino gli effetti delle collisioni e la presenza
del sistema di caching.
28
Studente ha 15 tuple per blocco e ogni accesso costa una operazione di i/o.
Esame ha 256 tuple per blocco ma non ha una struttura di acceso primaria a hash.
Per costruire la struttura hash-based (è l’unico modo per poterla usare!) occorre scandire
l’intera tabella e inserire ogni tupla nel “bucket” opportuno. Per applicare la stessa funzione
di hash (ad esempio Matricola mod 10.000) dobbiamo anche conservare lo stesso numero di
"bucket", che però - helas - saranno meno “pieni” (le tuple occupavano 8K, diventano 10K).
Pagheremo quindi 8K accessi in lettura per leggere e ben 4M in scrittura, perché dobbiamo
trovare per ogni esame il bucket giusto in cui inserirlo, e tale blocco dev’essere prima letto,
poi modificato aggiungendo l’esame nel punto giusto, e infine riscritto.
Lo hash-join poi costa 2 * 10K = 20K accessi per leggere i bucket via via associati allo
stesso valore della funzione di hash, per un totale di 4M + 28K accessi (è un costo notevole,
che però potrebbe essere ripagato col tempo per le successive esecuzioni dello stesso join se
si conserva la struttura a hash creata per l’occasione).
29
Calcoliamo ora il costo del nested loop con Esame come tabella interna (cioè nel caso in cui
leggiamo un blocco della tabella Studente (iterazione esterna) e per ogni studente cerchiamo in
Esame (iterazione interna) tutti gli esami sostenuti da quel particolare studente. Leggiamo un
nuovo blocco di Studente solo dopo aver esaurito tutti gli studenti nel blocco corrente).
L’albero ha profondità media pari a circa 3 ed è relativamente sparso (ha spazio per circa 8M
valori di chiave). La ricerca degli esami di un dato studente costa quindi 3 accessi ai nodi
dell’albero, più 1 accesso ai blocchi della struttura primaria entry-sequenced per recuperare
tutti i campi dell’esame (trascuriamo il caso in cui i puntatori agli esami di uno stesso studente
siano ripartiti su più di un nodo foglia dell’albero). In totale pagheremo 3 accessi per ogni
studente e uno per ogni esame, cioè 3*150K + 2M accessi.
Il caricamento di tutti i blocchi di Studente nell’iterazione esterna costa inoltre 10K accessi,
per cui in totale avremo = 2M + 460K accessi.
È un costo inferiore a quello di costruirsi la struttura ad-hoc (poco più di metà), il quale però
può ancora considerarsi un buon investimento se, conservando la struttura creata, velocizza poi
sostanzialmente le esecuzioni dello stesso join (20K contro 2,5M).
30
Calcoliamo infine, per completezza di analisi, benché non fosse richiesto dal testo, il costo del
nested loop con Esame come tabella esterna (che a prima vista sembra l’opzione più sensata,
dato che gli accessi random basati su matricola alla struttura primaria a hash dello Studente
costano molto meno di quelli a Esame basati sull’indice B+ (1 contro 4)).
Possiamo scandire tutta la tabella Esame pagando solo 8K accessi per la sua lettura,
sfruttando la struttura entry-sequenced (non avrebbe senso, infatti, scandire in sequenza le foglie
dell’indice B+ e accedere ai blocchi primari “uno studente alla volta”, dato che avere gli esami
in ordine di matricola non ci aiuta), e dobbiamo poi accedere 2M volte alla struttura hashed,
una volta per ogni esame, a recuperare i dati dello studente (più volte lo stesso studente, ogni
volta che ci imbattiamo in un suo esame – ma stiamo trascurando gli effetti della cache), per un
totale di 2M+8K accessi.
Il costo è minore, ma comunque dello stesso ordine di grandezza di quelli precedentemente
calcolati. Un ottimizzatore “miope” potrebbe continuare a scegliere questa ultima opzione
senza mai “imbarcarsi” nell’impresa di costruire la struttura hash-based, che invece si rivela
strategicamente la più conveniente se il join viene ripetuto frequentemente (come è probabile).