Arboles B

Anuncio
UTN Córdoba
Ingenieria en Sistemas de Información
Cátedra: Diseño de Lenguajes de Consulta
Ficha de clase número: 09
Fecha:
Docente:
Semana del 04/05 al 08/05 de 2009
Ing. Valerio Frittelli
Tema Principal:
Temas Particulares:
1.)
Indexación de Archivos: Arboles B.
Concepto de indexación. Indices basados en Arboles B. Reglas de
formación de un árbol B.
Indexación de archivos: conceptos básicos.
Al almacenar registros en un archivo, tarde o temprano se presenta la necesidad de
realizar una búsqueda de un registro particular en el archivo, y se pretende que esa búsqueda
sea rápida. Hemos visto que si el archivo está ordenado podemos hacer una búsqueda binaria
(y obtener un tiempo de búsqueda O(log(n), siendo n la cantidad de registros del archivo).
Mejor aún: si organizamos el archivo como una tabla hash obtendremos tiempos de búsqueda
aún mejores (en tiempo casi constante...) Entonces, no parece que haya nada que agregar al
tema...
Sin embargo, notemos que en las soluciones propuestas se debe reorganizar el
contenido del archivo para favorecer una búsqueda rápida. En el caso de la búsqueda binaria,
la reorganización consiste simplemente en ordenarlo. Y en el caso del hash, hay que
replantear por completo la estructura interna del archivo, al punto que un recorrido secuencial
del mismo pasa a carecer de sentido. El punto, entonces, no es menor: no siempre se querrá
cambiar la estructura del archivo, o no siempre será de ayuda hacerlo, o directamente podría
no ser posible hacerlo. ¿Qué pasaría si la estrategia para buscar rápido fuera mantener
ordenado el archivo por un atributo, pero luego se necesitara buscar por otros atributos? ¿Qué
pasaría si el archivo estuviera organizado como tabla hash y se requirieran recorridos
secuenciales (ordenados o no...)? En definitiva: ¿qué hacer si el archivo se organiza de
acuerdo a ciertas necesidades de consulta, pero luego se requieren vistas distintas del
contenido del archivo?
Piense que esto es lo que típicamente ocurre en una tabla de una base de datos: algunas
aplicaciones necesitan buscar por legajo, otras por apellido y nombre, otras por dirección
postal, etc. Sencillamente, no sería práctico que el archivo cambie su ordenamiento interno o
su estructura cada vez que se dispara una consulta de naturaleza diferente, y mucho menos se
puede aceptar que el archivo se "replique" varias veces (una por cada tipo de consulta
posible...)
La solución clásica para estos problemas de "vistas múltiples" para el mismo contenido
del archivo, son los archivos índice, o simplemente, índices. En un sentido amplio, un índice
es una estructura de datos (normalmente almacenada en memoria secundaria) que permite un
rápido acceso a los elementos contenidos en otra estructura (que típicamente es otro archivo).
Puede establecerse una analogía entre el índice de un libro y el libro mismo: si queremos
saber en qué página está un tema dado, se busca en el índice (en lugar de hacer un recorrido
secuencial página a página en el libro) y el índice nos dice en qué página buscar.
Típicamente un índice se organiza como un árbol de búsqueda balanceado, de forma
que garantice búsquedas de orden logarítmico. La idea es que si m es un archivo en el que se
quiere hacer búsquedas mediante índices (o simplemente, busquedas indexadas) y t es un
índice para m, entonces t contendrá un nodo por cada registro de m. Si cada registro contiene
un atributo k mediante el cual se quiere hacer la búsqueda, entonces cada nodo de t, contendrá
1
UTN Córdoba
Ingenieria en Sistemas de Información
Cátedra: Diseño de Lenguajes de Consulta
el valor del atributo k y mediante ese valor se ordenará el árbol. Además, cada nodo en t
tendrá la dirección en m del registro con el valor respectivo del atributo k (el número relativo
del registro que contiene a ese valor en k):
25
índice1:
ordenado
por legajo
archivo
20
30
legajo
30
25
20
Ana
Juan
Pedro
nombre
2000
1200
1500
sueldo
índice2:
Juan
ordenado
por nombre
Ana
Pedro
Como se ve, se puede tener varios índices a la vez sobre el mismo archivo, organizando
a cada índice por un atributo de búsqueda diferente, sin tocar la estructura del archivo
original, y sin que esos índices dependan el uno del otro...
Si bien en la gráfica mostramos a cada índice como un árbol binario, está claro que en
la práctica un índice para un archivo debe cumplir ciertos requisitos esenciales, pues de otro
modo el proceso de indexación fallaría o no tendría un tiempo de búsqueda aceptable. Por lo
pronto, el árbol de búsqueda no debería ser binario: si el archivo a indexar tiene una gran
cantidad de registros (lo cual en la práctica es lo común...) entonces el árbol tendría
demasiados niveles y por lo tanto su altura sería también grande. Aún cuando se supone que
el árbol implementa algún mecanismo de equilibrado, una gran altura va en contra de un
tiempo aceptable de búsqueda.
Y por otra parte, parece obvio que el árbol no debería organizarse en memoria
principal: si el archivo es muy grande el índice podría no entrar en memoria, y aún si entra
podría no quedar lugar para otros índices que se requieran para ese achivo.
Hasta 1972 no se había ideado una estructura de índice que realmente fuera eficiente en
el acceso rápido a archivos. Las diversas técnicas de equilibrado de árboles clásicas (como los
árboles AVL), eran claramente insuficientes para indexar grandes archivos. Pero entonces
algo cambió...
2
UTN Córdoba
Ingenieria en Sistemas de Información
Cátedra: Diseño de Lenguajes de Consulta
2.)
Arboles B.
En 1972, dos desarrolladores llamados R. Bayer y E. McCreight, trabajando para la
Boeing Corporation, publicaron un artículo (con el título de "Organization and Maintenance
of Large Ordered Files") en el número 1 de la revista especializada Acta Informática que
pasaría a la historia como el artículo de presentación de los Arboles B. Dos o tres décadas más
tarde, los árboles B eran ya el estándar indiscutido en cuanto a soporte de índices para acceso
rápido a grandes archivos de registros. El contenido de la publicación original de estos dos
autores, se anexa en un documento pdf adjunto a esta ficha.
Un árbol B es esencialmente un árbol de búsqueda multicamino (n-ario) balanceado,
almacenado en disco, y usado como soporte muy eficiente para índices de grandes archivos.
Garantiza un tiempo de búsqueda de orden logarítmico, al mismo tiempo que logra un muy
buen balance en cuanto al uso del espacio de almacenamiento del índice. Hoy en día los
árboles B están en el corazón de todos los sistemas de indexación de bases de datos
comerciales, aunque en forma de variantes respecto del planteo original de Bayer y
McCreight.
Anecdóticamente, digamos que sus creadores nunca dijeron porqué llamaron B al árbol
B... Se ha especulado con que esa B viene de balanceado. Otras especulaciones sugieren que
la B es una especie de propaganda subliminal a la firma Boeing para la que ambos trabajaban.
Y muchos directamente han asumido que la B es por Bayer, llegando incluso a designar como
árboles de Bayer a los árboles B... Pero si bien esto último parece un justo tributo a R. Bayer,
la pregunta que nos hacemos es: ¿qué hubo de McCreight en ese caso?
Como se dijo, un árbol B es un árbol de búsqueda de caminos múltiples. Esto implica
que cada nodo del árbol puede tener varios hijos, y a su vez implica que cada nodo puede
tener varias claves o valores (de hecho, la forma típica de implementar el conjunto de claves
de un nodo es a través de un arreglo ordenado). En el léxico propio de los árboles B, un nodo
se designa como página. La idea central es que un árbol B se graba en un archivo (el archivo
índice) página por página. Cuando el índice se abre, se carga en memoria la página raiz del
árbol, y sólo la página raiz. La clave deseada se busca en esa página, y si está en ella se
detiene el proceso (que en este caso sólo llevó una operación de acceso a disco). Si la clave
no está, se determina en cuál de las hijas de la raiz debería estar, y se carga esa página. Otra
vez, se busca la clave, y así se prosigue, de forma que la raiz siempre esté en memoria y cada
nueva página que se carga reemplace a la última cargada. Si la cantidad de claves que se
almacena en cada página es adecuada, veremos que se puede garantizar que un árbol B
encontrará la clave (o verá que no existe) en no más de dos accesos directos (seeking) a disco
incluso si el archivo tiene un número tan desopilante como un millón de registros...
Las características estructurales de un árbol B se resumen en cuatro reglas, que
enunciamos:
i.)
ii.)
Sea n un parámetro que designaremos como el orden del árbol. Entonces cada página del
árbol (salvo eventualmente la página raiz) debe tener un mínimo de n claves en todo momento.
El número máximo de claves que una página puede tener es 2*n.
iii.)
Hay sólo dos tipos de páginas: las hojas (que no tienen hijos) y las páginas que tienen
exactamente m + 1 páginas hijas, siendo m el número de claves que efectivamente hay en esa
página.
iv.)
Todas las páginas hoja deben aparecer juntas en el mismo nivel (que no puede ser otro que el
último nivel... como es obvio...)
3
UTN Córdoba
Ingenieria en Sistemas de Información
Cátedra: Diseño de Lenguajes de Consulta
Estas cuatro reglas indican en forma taxativa lo que se puede y no se puede hacer al ir
formando un árbol B. Veremos a continuación, que cada una de ellas cumple un papel
importante en cuanto al proceso de auto-balanceo del árbol: al igual que un árbol AVL, los
árboles B insertan una clave y luego verifican si esa inserción provocó algún tipo de
desbalance, procediendo a restaurar el equilibrio si fuera el caso.
Supongamos que se quiere mostrar la forma en que va creciendo un árbol B de orden 2
(n=2) a medida que se insertan claves. Supongamos que el árbol arranca vacío, y que se
quieren insertar las siguientes primeras cuatro claves: 10, 20, 40 y 30. Como el árbol está
vacío, no contiene ninguna página aún. Debemos crear la página raiz entonces. La regla ii)
nos dice que debemos prever lugar máximo para 2*n claves = 4 claves. Lo común es dibujar
cada página como si fuera un arreglo de tamaño 2*n. Las claves que se insertan (al igual que
en un árbol de búsqueda común) se insertan como parte de una hoja, y dentro de la hoja
huésped se almacenan ordenadas de menor a mayor. La raiz quedaría así luego de crearla e
insertar las primeras cuatro claves citadas (note que la regla i exige que una página siempre
tenga al menos n = 2 claves, pero la raiz está exenta de esa regla: de otro modo, nunca
podríamos tener un árbol B vacío o con menos de n claves cuando recién se crea...)
10
20
30
40
Supongamos que ahora queremos insertar la clave x = 25. La única página que tiene el
árbol ya no tiene lugar para una nueva clave, por lo tanto debemos crear al menos una página
nueva. Sin embargo, no podemos crear páginas de cualquier forma... Las reglas estructurales
del árbol sugieren que hay sólo una forma de hacerlo: suponga que x = 25 realmente pudo
insertarse. En ese caso, la secuencia ordenada quedaría así:
promover al nivel superior
10
20
25
30
40
Puede verse que ahora el número de claves es impar (2*n + 1 claves) y por lo tanto el
valor del medio de la secuencia es la mediana de la misma. Lo que se hace es tomar el valor
mediano, y promoverlo a la página que estuviera en el nivel superior de esta que fue
rebalsada. Si no hubiera ninguna página en el nivel superior (pues la página rabalsada es la
raiz) entonces se crea una nueva raiz que sólo contendrá al valor promovido (el 25 en nuestro
caso). Pero en la página original quedan exactamente 2*n claves, tales que la mitad son
menores que el promovido y la otra mitad son mayores... se arman dos páginas con esos
valores, que quedarán como hijas de la raiz:
25
10
20
menores que 25
30
40
mayores que 25
4
UTN Córdoba
Ingenieria en Sistemas de Información
Cátedra: Diseño de Lenguajes de Consulta
Como se ve, en el árbol resultante se cumplen todas las reglas: las dos páginas hoja
están juntas en el último nivel. Ninguna página (salvo la raiz) tiene menos de n = 2 claves. Y
la única página que tiene hijos, tiene exactamente 2 hijos, los cual es igual al número m de
claves que tiene esa página (la raiz, con m = 1 clave efectivamente contenida) más uno.
Note que el árbol ha crecido un nivel: de altura h = 1 mientras sólo tenía cuatro claves,
pasó a tener altura h = 2 con la quinta clave. Veremos que en el único caso en que el árbol
gana altura es cuando se parte la raiz (lo que ocurrió en este caso...)
Supongamos que ahora se quieren insertar las claves 5, 15 y 23. Las claves entran por
la raiz, y bajan por el árbol buscando una hoja con lugar libre para insertarse. Las claves 5 y
15 no tendrán problemas, pero la clave 23 provocará un desborde de la página izquierda del
25:
25
5
10
20
15
30
23
40
mayores que 25
menores que 25
El 15 es el mediano en la hoja desbordada, y por lo tanto debe promoverse al nivel
superior. En este caso, en ese nivel hay una página (la raiz) que tiene lugar libre. Por lo tanto,
el 15 se alojará en esa página. Como ahora la raiz tendrá m = 2 claves efectivas, no podrá
seguir teniendo sólo un hijo: deberá tener m + 1 = 3 hijos. Pero esto no es problema: la pagina
desbordada se partió en dos, quedando la mitad de las claves menores que 15 y la otra mitad
mayores. Esas claves arman dos páginas que quedarán como hijos del 15. La página con el 30
y el 40 no sufre cambios:
15
5
10
menores que 15
25
20
23
mayores que 15 y
menores que 25
30
40
menores que 15
Observe que se partió una página pero el árbol no ganó altura: el impacto del quiebre de
la página fue absorbido "a lo ancho" en el árbol (lo cual no produce merma en el rendimiento
de una búsqueda: mientras no crezca la altura, el crecimiento horizontal no es problema).
Podemos decir entonces que los árboles B son muy estables en cuanto a su crecimiento en
altura: debe quebrarse la raiz para que la altura crezca, pero ese acontecimiento depende que
primero se quiebre una larga serie de páginas inferiores antes de provocar el rebalsamiento en
5
UTN Córdoba
Ingenieria en Sistemas de Información
Cátedra: Diseño de Lenguajes de Consulta
la raiz... De hecho, en este momento el árbol necesitaría el quiebre de tres páginas para que la
raiz se quiebre a su vez... Para verlo, adelantémonos en el proceso. Supongamos que ya se han
insertado las claves 45, 50 y 60 (provocando un quiebre y enviando el 45 a la raiz) y luego las
claves 65, 70 y 80 (provocando otro quiebre y enviando el 65 a la raiz). El árbol quedaría así:
15
5
10
20
25
23
45
65
30
40
50
60
70
80
Si ahora se insertan las claves 85, 90 y 95, el valor 95 provocará el desborde de la
última página de la derecha del árbol. En ese momento el 85 quedará como mediano y será
promovido al nivel superior. Pero en ese nivel, la raiz no tiene lugar y se desborda a su vez,
provocando que el 45 sea promovido hacia arriba: antes de las particiones, el árbol quedaría
así:
15
5
10
20
25
23
45
65
30
40
85
50
60
70
80
85
90
La solución: se crea una nueva raiz con el 45. El árbol crece un nivel, y las páginas se
reparten así:
45
15
5
10
20
25
23
65
30
40
50
60
85
70
80
90
95
En nuestro modelo hemos supuesto que el orden n del árbol es 2, y eso sirve a los
efectos didácticos. Pero en la práctica, el orden del árbol se elige de forma tal que cada página
pueda tener un número alto de claves: De hecho, se suele hacer que el orden elegido garantice
que el tamaño final en bytes de una página sea un múltiplo de 512, de forma que el tamaño de
6
95
UTN Córdoba
Ingenieria en Sistemas de Información
Cátedra: Diseño de Lenguajes de Consulta
una página coincida o sea múltiplo del factor de paginación usado por el sistema operativo
huésped: de esta forma, se aprovecha al máximo la capacidad de lectura y grabación del
sistema.
Note que si el orden del árbol es n = 500, entonces una página puede tener hasta 1000
claves. Si la raiz tiene 1000 claves, deberá tener 1001 páginas hijas que pueden a su vez tener
hasta 1000 claves cada una... Eso implica que el árbol podrá contener un millón de claves... en
sólo dos niveles de árbol. En la práctica, la página raiz está siempre en memoria mientras el
índice está abierto (sólo se accede a disco para cargarla la primera vez, cuando el índice se
abre). Por lo tanto, encontrar una clave requerirá como máximo dos operaciones de acceso
directo a disco (que en la práctica es sólo una... pues dijimos que la raiz sólo se levanta de
disco una vez, al principio...)
Esa notable eficiencia se ve incrementada con otras características destacables: la regla
i estructural del árbol garantiza que al ir a disco a buscar una página, esa página tendrá un
número razonable de claves (50% llena) que haga que valga la pena la pérdida de tiempo de ir
al disco a buscarla. Y la regla ii nos protege de la posibilidad de levantar una página tan llena
de claves que no quepa en memoria. La regla iii garantiza que no iremos a disco nunca a
buscar una página vacía. Y la iv es la que finalmente fuerza a que el árbol se mantenga
siempre en equilibrio, y se expanda hacia lo ancho en lugar de hacerlo hacia arriba.
3.)
Variantes a los árboles B.
Desde 1972 a la fecha se han estudiado a fondo las propiedades de los árboles B, y
como no podía ser de otra forma, se plantearon muchas variantes estructurales. Una de ellas
fue planteada por Knuth en 1973, y se llamó Arbol B* (B estrella o B star). Esencialmente se
trata de una variante que garantiza que cada página tendrá al menos dos tercios de su
capacidad ocupada (y no sólo la mitad)
Otra variante muy conocida es la designada como árbol B+ (B más o B plus). La idea
es simple: un árbol B ofrece un posibilidad rápida de acceder a un registro del archivo, pero
no ofrece la propiedad de recorrer en forma secuencial ordenada ese mismo archivo. Los
árboles B+ constituyen una extensión natural al árbol B permitiendo esa característica. En un
árbol B+, todas las claves se almacenan en la hojas del último nivel. Las páginas intermedias
contienen valores duplicados de esas claves, sólo para proporcionar rutas de recorrido de
búsqueda que lleguen hasta las hojas. Hasta aquí, el árbol sigue sirviendo para búsquedas
rápidas. Pero el detalle final es que las hojas del último nivel se enlazan entre ellas formando
una lista, que puede recorrerse desde el principio (si entramos al archivo por la clave menor) o
desde cualquier otra parte (entrando al índice por una clave cualquiera, llegamos hasta ella en
la hoja del último nivel y recorremos desde alli en adelante en orden de lista):
un árbol B+
10
5
10
30
20
23
30
40
7
UTN Córdoba
Ingenieria en Sistemas de Información
Cátedra: Diseño de Lenguajes de Consulta
Bibliografía: Si bien los profesores de la cátedra preparan una serie de fichas de
consulta y guía para los temas de la asignatura, debe entenderse que para un
dominio completo de estos temas y para el desarrollo óptimo de las tareas y
ejercicios que se piden es fuertemente recomendable que los alumnos estudien e
investiguen a fondo en otras fuentes. Va para ello la siguiente bibliografía de
consulta y ampliación de temas:

Deitel, H., Deitel, P. (2005 o posterior). "Java Cómo Programar" . México: Prentice Hall.
ISBN: 970-26-0518-0 [disponible en biblioteca del Departamento de Sistemas]

Drozdek, A. (2007). "Estructura de Datos y Algoritmos en Java". México D.F.: Thomson.
ISBN: 9789706866110 [disponible en biblioteca del Departamento de Sistemas]

Eckel, B. (2002 aunque existe edición posterior). "Piensa en Java". Madrid: Pearson
Educación. ISBN: 9788489660342. [disponible en biblioteca del Departamento de Sistemas]

Horstmann, C., y Cornell G. (2000). “Core Java 2 – Volume I: Fundamentals”. (Disponible en
español) Upper Saddle River: Prentice Hall. ISBN: 84-205-4832-4 [disponible en biblioteca
central]

Horstmann, C., y Cornell G. (2001). “Core Java 2 – Volume II: Advanced Features”.
(Disponible en español) Palo Alto: Prentice Hall. ISBN: 84-8322-310-4 [disponible en
biblioteca central]

Langsam, Y., Augenstein, M., y Tenenbaum, A. (1997). “Estructura de Datos con C y C++
(2da. Edición)”. México: Prentice Hall. ISBN: 968-880-798-2 [disponible en biblioteca
central]

Sedgewick, Robert (1995). “Algoritmos en C++”. Reading: Addison Wesley – Díaz de
Santos. ISBN: 978-0-201-62574-5 [disponible en biblioteca central]

Weiss, M. A. (2000). “Estructuras de Datos en Java – Compatible con Java 2”. Madrid:
Addison Wesley. ISBN: 84-7829-035-4 [disponible en biblioteca central]
8
Descargar