El hash extensible es un tipo de sistema hash que trata un hash como una cadena de bits y utiliza un trie para la búsqueda de cubetas. [ 1 ] Debido a la naturaleza jerárquica del sistema, el rehashing es una operación incremental (se realiza una cubeta a la vez, según sea necesario). Esto significa que las aplicaciones sensibles al tiempo se ven menos afectadas por el crecimiento de la tabla que por los rehashes estándar de tabla completa.
El hash extensible fue descrito por Ronald Fagin en 1979. Prácticamente todos los sistemas de archivos modernos utilizan hash extensible o árboles B. En particular, el Sistema Global de Archivos (GPFS) , ZFS y el sistema de archivos SpadFS utilizan hash extensible. [ 2 ]
Ejemplo
Supongamos que la función hashDevuelve una cadena de bits. El primeroLos bits de cada cadena se utilizarán como índices para determinar dónde irán en el "directorio" (tabla hash), dondees el número más pequeño tal que el índice de cada elemento en la tabla es único.
Teclas a utilizar:
Supongamos que, para este ejemplo en particular, el tamaño del cubo es 1. Las dos primeras claves que se insertarán, k 1 y k 2 , se pueden distinguir por el bit más significativo y se insertarían en la tabla de la siguiente manera:
Ahora bien, si se aplicara la función hash a k3 en la tabla, no bastaría con distinguir las tres claves por un bit (ya que tanto k3 como k1 tienen 1 como su bit más a la izquierda). Además, como el tamaño del cubo es uno, la tabla se desbordaría. Dado que comparar los dos primeros bits más significativos daría a cada clave una ubicación única, el tamaño del directorio se duplica de la siguiente manera:
Así pues, ahora k 1 y k 3 tienen una ubicación única, distinguiéndose por los dos primeros bits de la izquierda. Dado que k 2 se encuentra en la mitad superior de la tabla, tanto 00 como 01 apuntan a él, ya que no existe ninguna otra clave con la que comparar que comience con un 0.
El ejemplo anterior proviene de Fagin et al. (1979) .
Más detalles
Ahora, se debe insertar k 4 , y tiene los dos primeros bits como 01..(1110), y usando una profundidad de 2 bits en el directorio, esto se asigna de 01 al Cubo A. El Cubo A está lleno (tamaño máximo 1), por lo que debe dividirse; como hay más de un puntero al Cubo A, no hay necesidad de aumentar el tamaño del directorio.
Lo que se necesita es información sobre:
- El tamaño de la clave que mapea el directorio (la profundidad global), y
- El tamaño de la clave que previamente ha mapeado el cubo (la profundidad local)
Para distinguir los dos casos de acción:
- Duplicar el directorio cuando un depósito se llena
- Crear un nuevo depósito y redistribuir las entradas entre el depósito antiguo y el nuevo.
Al examinar el caso inicial de una estructura hash extensible, si cada entrada de directorio apunta a un bucket, entonces la profundidad local debería ser igual a la profundidad global.
El número de entradas del directorio es igual a 2 de profundidad global , y el número inicial de cubetas es igual a 2 de profundidad local .
Por lo tanto, si la profundidad global = profundidad local = 0, entonces 2 0 = 1, por lo que un directorio inicial de un puntero a un cubo.
Volvamos a los dos casos de acción; si el cubo está lleno:
- Si la profundidad local es igual a la profundidad global, entonces solo hay un puntero al bucket, y no hay otros punteros de directorio que puedan mapearse al bucket, por lo que el directorio debe duplicarse.
- Si la profundidad local es menor que la profundidad global, entonces existe más de un puntero desde el directorio al bucket, y el bucket puede dividirse.
La clave 01 apunta al Cubo A, y la profundidad local del Cubo A de 1 es menor que la profundidad global del directorio de 2, lo que significa que las claves hasheadas al Cubo A solo han usado un prefijo de 1 bit (es decir, 0), y el cubo necesita tener su contenido dividido usando claves de 1 + 1 = 2 bits de longitud; en general, para cualquier profundidad local d donde d es menor que D, la profundidad global, entonces d debe incrementarse después de una división del cubo, y el nuevo d se usa como el número de bits de la clave de cada entrada para redistribuir las entradas del cubo anterior en los nuevos cubos.
Ahora, Se intenta de nuevo, con 2 bits 01.., y ahora la clave 01 apunta a un nuevo cubo, pero aún hayen él (y también comienza con 01).
SiSi hubiera sido 000110, con la clave 00, no habría habido problema, porque habría permanecido en el nuevo cubo A' y el cubo D habría estado vacío.
(Este habría sido, con diferencia, el caso más probable cuando los depósitos son de tamaño superior a 1 y es extremadamente improbable que los depósitos recién divididos se desborden, a menos que todas las entradas se volvieran a combinar en un solo depósito. Pero para enfatizar la importancia de la información de profundidad, el ejemplo se desarrollará lógicamente hasta el final).
Por lo tanto, el Cubo D debe dividirse, pero una comprobación de su profundidad local, que es 2, es la misma que la profundidad global, que también es 2, por lo que el directorio debe dividirse de nuevo para poder almacenar claves con suficiente detalle, por ejemplo, de 3 bits.
- El cubo D debe dividirse porque está lleno.
- Como la profundidad local de D es igual a la profundidad global, el directorio debe duplicarse para aumentar el detalle de bits de las claves.
- La profundidad global ha aumentado a 3 después de la división del directorio.
- La nueva entrada se vuelve a cambiar la clave con una profundidad global de 3 bits y termina en D que tiene una profundidad local de 2, que ahora se puede incrementar a 3 y D se puede dividir en D' y E.
- El contenido del cubo dividido D , , se ha vuelto a cambiar la clave con 3 bits y termina en D'.
- Se vuelve a intentar con K4 y termina en E, que tiene una ranura libre.
Ahora,está en D ySe intenta de nuevo, con 3 bits 011.., y apunta al cubo D que ya contiene así que está lleno; la profundidad local de D es 2 pero ahora la profundidad global es 3 después de la duplicación del directorio, por lo que ahora D se puede dividir en los buckets D' y E, el contenido de D, tiene su Se volvió a intentar con una nueva máscara de bits de profundidad global de 3 y termina en D', luego la nueva entrada Se vuelve a intentar conSe aplica una máscara de bits utilizando el nuevo conteo global de bits de profundidad de 3, lo que da como resultado 011, que ahora apunta a un nuevo cubo E que está vacío. Por lo tanto , va en el Cubo E.
Ejemplo de implementación
A continuación se muestra el algoritmo de hash extensible en Python , con la asociación de bloques de disco/páginas de memoria, el almacenamiento en caché y los problemas de consistencia resueltos. Cabe destacar que existe un problema si la profundidad excede el tamaño en bits de un entero , ya que, en ese caso, duplicar el directorio o dividir un bucket impedirá que las entradas se vuelvan a hashear en diferentes buckets.
El código utiliza los bits menos significativos , lo que hace que sea más eficiente expandir la tabla, ya que todo el directorio se puede copiar como un solo bloque ( Ramakrishnan y Gehrke (2003) ).
Ejemplo de Python
TAMAÑO_DE_PÁGINA = 10clase Page : def __init __ ( self ) - > None : self.map = [ ] self.local_depth = 0def full ( self ) - > bool : return len ( self.map ) > = PAGE_SZdef put ( self , k , v ) - > None : for i , ( key , value ) in enumerate ( self.map ) : if key == k : del self.map [ i ] break self.map.append ( ( k , v ) )def obtener ( self , k ) : para clave , valor en self.map : si clave == k : devolver valordef get_local_high_bit ( self ): return 1 << self . local_depthclase ExtendibleHashing : def __init __ ( self ) - > None : self.global_depth = 0 self.directory = [ Page ( ) ]def get_page ( self , k ): h = hash ( k ) return self . directory [ h & (( 1 << self . global_depth ) - 1 )]def put ( self , k , v ) - > None : p = self.get_page ( k ) full = p.full ( ) p.put ( k , v ) if full : if p.local_depth == self.global_depth : self.directory * = 2 self.global_depth + = 1p0 = Page ( ) p1 = Page ( ) p0.local_depth = p1.local_depth = p.local_depth + 1 high_bit = p.get_local_high_bit ( ) for k2 , v2 in p.map : h = hash ( k2 ) new_p = p1 if h & high_bit else p0 new_p.put ( k2 , v2 )for i in range ( hash ( k ) & ( high_bit - 1 ), len ( self . directory ), high_bit ): self . directory [ i ] = p1 if i & high_bit else p0def obtener ( self , k ): return self . obtener_página ( k ) . obtener ( k )if __name__ == "__main__" : eh = ExtendibleHashing () N = 10088 l = list ( range ( N ))import random random.shuffle ( l ) for x in l : eh.put ( x , x ) print ( l )para i en rango ( N ): imprimir ( eh . obtener ( i ))Notas
- ↑ Fagin et al. (1979) .
- ↑ Mikuláš Patocka (2006). Diseño e implementación del sistema de archivos Spad (PDF) (Tesis). Archivado del original (PDF) el 15 de marzo de 2016. Consultado el 27 de febrero de 2016 ."Sección 4.1.6 Hashing extensible: ZFS y GFS" y "Tabla 4.1: Organización de directorios en sistemas de archivos"
Véase también
Referencias
- Fagin, R.; Nievergelt, J.; Pippenger, N.; Strong, HR (septiembre de 1979), "Extendible Hashing - A Fast Access Method for Dynamic Files", ACM Transactions on Database Systems , 4 (3): 315– 344, doi : 10.1145/320083.320092 , S2CID 2723596
- Ramakrishnan, R.; Gehrke, J. (2003), Sistemas de gestión de bases de datos, 3.ª edición: Capítulo 11, Indexación basada en hash , págs. 373–378
- Silberschatz, Abraham ; Korth, Henry ; Sudarshan, S., Conceptos de sistemas de bases de datos, sexta edición: capítulo 11.7, Hashing dinámico
Enlaces externos
Este artículo incorpora material de dominio público de Paul E. Black. "Extendible hashing" . Diccionario de algoritmos y estructuras de datos . NIST .- Notas sobre el hash extensible en la Universidad Estatal de Arkansas.
- Notas de hash extensibles Archivadas el 3 de marzo de 2016 en la Wayback Machine
- Diapositivas del libro "Conceptos de sistemas de bases de datos" sobre el hash extensible para índices dinámicos basados en hash.
- Algoritmos de búsqueda
- Hashing