sql >> Base de Datos >  >> NoSQL >> HBase

HBase BlockCache 101

Esta entrada de blog se publicó en Hortonworks.com antes de la fusión con Cloudera. Es posible que algunos enlaces, recursos o referencias ya no sean precisos.

Esta publicación de blog apareció originalmente aquí y se reproduce en su totalidad aquí.

HBase es una base de datos distribuida creada en torno a los conceptos básicos de un registro de escritura ordenado y un árbol de combinación con estructura de registro. Al igual que con cualquier base de datos, la E/S optimizada es una preocupación crítica para HBase. Cuando sea posible, la prioridad es no realizar ninguna E/S en absoluto. Esto significa que la utilización de la memoria y las estructuras de almacenamiento en caché son de suma importancia. Con este fin, HBase mantiene dos estructuras de caché:el "almacén de memoria" y el "caché de bloque". Almacén de memoria, implementado como MemStore , acumula ediciones de datos a medida que se reciben, almacenándolas en la memoria (1). La caché de bloques, una implementación de BlockCache interfaz, mantiene los bloques de datos residentes en la memoria después de leerlos.

La MemStore es importante para acceder a ediciones recientes. Sin MemStore , acceder a esos datos tal como se escribieron en el registro de escritura requeriría leer y deserializar las entradas de ese archivo, al menos un O(n) operación. En su lugar, MemStore mantiene una estructura de lista salteada, que disfruta de un O(log n) costo de acceso y no requiere E/S de disco. La MemStore sin embargo, contiene solo una pequeña parte de los datos almacenados en HBase.

Mantenimiento de lecturas desde BlockCache es el mecanismo principal a través del cual HBase puede servir lecturas aleatorias con una latencia de milisegundos. Cuando se lee un bloque de datos de HDFS, se almacena en caché en BlockCache . Las lecturas posteriores de datos vecinos (datos del mismo bloque) no sufren la penalización de E/S de recuperar nuevamente esos datos del disco (2). Es el BlockCache ese será el enfoque restante de esta publicación.

Bloques para almacenar en caché

Antes de entender el BlockCache , ayuda a comprender qué es exactamente un "bloque" HBase. En el contexto de HBase, un bloque es una sola unidad de E/S. Al escribir datos en un HFile, el bloque es la unidad de datos más pequeña escrita. Del mismo modo, un solo bloque es la cantidad más pequeña de datos que HBase puede leer de un HFile. Tenga cuidado de no confundir un bloque HBase con un bloque HDFS o con los bloques del sistema de archivos subyacente; todos son diferentes (3).

Los bloques HBase vienen en 4 variedades: DATAMETAINDEXBLOOM .

DATA Los bloques almacenan datos de usuario. Cuando BLOCKSIZE se especifica para una familia de columnas, es una sugerencia para este tipo de bloque. Eso sí, es sólo una pista. Mientras limpia el MemStore , HBase hará todo lo posible para respetar esta directriz. Después de cada Cell se escribe, el escritor comprueba si la cantidad escrita es>=el objetivo BLOCKSIZE . Si es así, cerrará el bloque actual y comenzará el siguiente (4).

INDEXBLOOM los bloques tienen el mismo objetivo; ambos se utilizan para acelerar la ruta de lectura. INDEX los bloques proporcionan un índice sobre la Cell s contenidos en los DATA bloques BLOOM los bloques contienen un filtro de floración sobre los mismos datos. El índice permite al lector saber rápidamente dónde se encuentra una Cell debe ser almacenado. El filtro le dice al lector cuando una Cell está definitivamente ausente de los datos.

Finalmente, META los bloques almacenan información sobre el propio HFile y otra información diversa:metadatos, como era de esperar. En Apache HBase I/O - HFile, se proporciona una descripción general más completa de los formatos de HFile y las funciones de varios tipos de bloques.

HBase BlockCache y sus implementaciones

Hay un solo BlockCache instancia en un servidor de región, lo que significa que todos los datos de todas las regiones alojadas por ese servidor comparten el mismo grupo de caché (5). El BlockCache se crea una instancia en el inicio del servidor de la región y se conserva durante toda la duración del proceso. Tradicionalmente, HBase proporcionaba solo un único BlockCache implementación:el LruBlockCache . La versión 0.92 introdujo la primera alternativa en HBASE-4027: SlabCache . HBase 0.96 introdujo otra opción a través de HBASE-7404, llamada BucketCache .

La diferencia clave entre el probado y verdadero LruBlockCache y estas alternativas es la forma en que manejan la memoria. Específicamente, LruBlockCache es una estructura de datos que reside completamente en el montón de JVM, mientras que los otros dos pueden aprovechar la memoria desde fuera del montón de JVM. Esta es una distinción importante porque la memoria de almacenamiento dinámico de JVM está gestionada por el recopilador de elementos no utilizados de JVM, mientras que los demás no. En los casos de SlabCacheBucketCache , la idea es reducir la presión de GC experimentada por el proceso del servidor de región al reducir la cantidad de objetos retenidos en el montón.

LruBlockCache

Esta es la implementación predeterminada. Los bloques de datos se almacenan en caché en el montón de JVM utilizando esta implementación. Se subdivide en tres áreas:acceso único, acceso múltiple y en memoria. Las áreas tienen un tamaño del 25 %, 50 %, 25 % del total BlockCache tamaño, respectivamente (6). Un bloque inicialmente leído de HDFS se llena en el área de acceso único. Los accesos consecutivos promueven ese bloque en el área de accesos múltiples. El área en memoria está reservada para bloques cargados desde familias de columnas marcadas como IN_MEMORY . Independientemente del área, los bloques antiguos se desalojan para dejar espacio a los bloques nuevos utilizando un algoritmo de uso menos reciente, de ahí el "Lru" en "LruBlockCache".

SlabCache

Esta implementación asigna áreas de memoria fuera del montón de JVM usando DirectByteBuffer s. Estas áreas proporcionan el cuerpo de este BlockCache . El área precisa en la que se colocará un bloque en particular se basa en el tamaño del bloque. De forma predeterminada, se asignan dos áreas, que consumen el 80 % y el 20 % del tamaño total de caché fuera del montón configurado, respectivamente. El primero se usa para almacenar en caché bloques que tienen aproximadamente el tamaño de bloque de destino (7). Este último contiene bloques que son aproximadamente 2 veces el tamaño del bloque de destino. Se coloca un bloque en el área más pequeña donde cabe. Si la memoria caché encuentra un bloque más grande de lo que cabe en cualquier área, ese bloque no se almacenará en la memoria caché. Me gusta LruBlockCache , el desalojo de bloques se gestiona mediante un algoritmo LRU.

Caché de depósito

Esta implementación se puede configurar para operar en uno de tres modos diferentes: heapoffheapfile . Independientemente del modo operativo, el BucketCache administra áreas de memoria llamadas "cubos" para almacenar bloques almacenados en caché. Cada cubo se crea con un tamaño de bloque de destino. El heap la implementación crea esos cubos en el montón de JVM; offheap la implementación utiliza DirectByteByffers para administrar cubos fuera del montón de JVM; file mode espera una ruta a un archivo en el sistema de archivos en el que se crean los cubos. file El modo está diseñado para usarse con un almacenamiento de respaldo de baja latencia:un sistema de archivos en memoria, o tal vez un archivo que se encuentra en el almacenamiento SSD (8). Independientemente del modo, BucketCache crea 14 cubos de diferentes tamaños. Utiliza la frecuencia de acceso a bloques para informar la utilización, al igual que LruBlockCache y tiene el mismo desglose de acceso único, acceso múltiple y en memoria del 25 %, 50 % y 25 %. Al igual que la memoria caché predeterminada, la expulsión de bloques se gestiona mediante un algoritmo LRU.

Almacenamiento en caché de varios niveles

Tanto el SlabCacheBucketCache están diseñados para ser utilizados como parte de una estrategia de almacenamiento en caché de varios niveles. Por lo tanto, una parte del total BlockCache el tamaño se asigna a un LruBlockCache instancia. Esta instancia actúa como caché de primer nivel, "L1", mientras que la otra instancia de caché se trata como caché de segundo nivel, "L2". Sin embargo, la interacción entre LruBlockCacheSlabCache es diferente de cómo LruBlockCache y el BucketCache interactuar.

El SlabCache estrategia, llamada DoubleBlockCache , es almacenar siempre en caché los bloques en las cachés L1 y L2. Los dos niveles de caché funcionan de forma independiente:ambos se comprueban al recuperar un bloque y cada uno expulsa bloques sin tener en cuenta al otro. El BucketCache estrategia, llamada CombinedBlockCache , utiliza la caché L1 exclusivamente para los bloques Bloom e Index. Los bloques de datos se envían directamente a la caché L2. En caso de expulsión del bloque L1, en lugar de descartarse por completo, ese bloque se degrada a la memoria caché L2.

¿Cuál elegir?

Hay dos razones para considerar habilitar una de las alternativas BlockCache implementaciones. El primero es simplemente la cantidad de RAM que puede dedicar al servidor de la región. La sabiduría de la comunidad reconoce que el límite superior del montón de JVM, en lo que respecta al servidor de la región, está entre 14 GB y 31 GB (9). El límite preciso generalmente depende de una combinación de perfil de hardware, configuración del clúster, forma de las tablas de datos y patrones de acceso a la aplicación. Sabrá que ha entrado en la zona de peligro cuando GC se detenga y RegionTooBusyException s comienzan a inundar sus registros.

El otro momento para considerar un caché alternativo es cuando la latencia de respuesta realmente asuntos. Mantener el montón alrededor de 8-12 GB permite que el recopilador de CMS funcione sin problemas (10), lo que tiene un impacto medible en el percentil 99 de los tiempos de respuesta. Dada esta restricción, las únicas opciones son explorar un recolector de basura alternativo o probar una de estas implementaciones fuera del montón.

Esta segunda opción es exactamente lo que he hecho. En mi próxima publicación, compartiré algunos resultados de experimentos no científicos pero informativos donde comparo los tiempos de respuesta para diferentes BlockCache implementaciones.

Como siempre, ¡estén atentos y continúen con HBase!

1:La MemStore acumula ediciones de datos a medida que se reciben, almacenándolas en la memoria. Esto tiene dos propósitos:aumenta la cantidad total de datos escritos en el disco en una sola operación y conserva esos cambios recientes en la memoria para el acceso posterior en forma de lecturas de baja latencia. Lo primero es importante ya que mantiene los fragmentos de escritura de HBase aproximadamente sincronizados con los tamaños de bloque de HDFS, alineando los patrones de acceso de HBase con el almacenamiento HDFS subyacente. Este último se explica por sí mismo y facilita las solicitudes de lectura de datos escritos recientemente. Vale la pena señalar que esta estructura no está involucrada en la durabilidad de los datos. Las ediciones también se escriben en el registro de escritura ordenado, el HLog , que implica una operación de adición de HDFS en un intervalo configurable, generalmente inmediato.

2:Volver a leer los datos del sistema de archivos local es el mejor de los casos. Después de todo, HDFS es un sistema de archivos distribuido, por lo que, en el peor de los casos, es necesario leer ese bloque a través de la red. HBase hace todo lo posible para mantener la ubicación de los datos. Estos dos artículos ofrecen una visión detallada de lo que significa la localidad de datos para HBase y cómo se gestiona.

3:Los bloques del sistema de archivos, HDFS y HBase son diferentes pero están relacionados. El subsistema de E/S moderno tiene muchas capas de abstracción encima de la abstracción. El núcleo de esa abstracción es el concepto de una sola unidad de datos, denominada "bloque". Por lo tanto, las tres capas de almacenamiento definen su propio bloque, cada una de su propio tamaño. En general, un tamaño de bloque más grande significa un mayor rendimiento de acceso secuencial. Un tamaño de bloque más pequeño facilita un acceso aleatorio más rápido.

4:Colocando el BLOCKSIZE comprobar después de escribir los datos tiene dos ramificaciones. Una sola Cell es la unidad de datos más pequeña escrita en un DATA cuadra. También significa una Cell no puede abarcar varios bloques.

5:Esto es diferente de MemStore , para el cual hay una instancia separada para cada región alojada por el servidor de regiones.

6:Hasta hace muy poco, estas particiones de memoria se definían estáticamente; no había forma de anular la división 25/50/25. Un segmento dado, el área de acceso múltiple, por ejemplo, podría crecer más que su asignación del 50% siempre que las otras áreas estuvieran infrautilizadas. El aumento de la utilización en las otras áreas desalojará las entradas del área de acceso múltiple hasta que se logre el equilibrio 25/50/25. El operador no pudo cambiar estos tamaños predeterminados. HBASE-10263, que se envía en HBase 0.98.0, introduce parámetros de configuración para estos tamaños. Se conserva el comportamiento flexible.

7:El negocio "aproximadamente" es permitir cierto margen de maniobra en los tamaños de bloque. El tamaño de bloque de HBase es un objetivo o una sugerencia aproximada, no una restricción estrictamente aplicada. El tamaño exacto de cualquier bloque de datos en particular dependerá del tamaño del bloque objetivo y del tamaño de la Cell valores contenidos en el mismo. La sugerencia de tamaño de bloque se especifica como el tamaño de bloque predeterminado de 64 kb.

8:Uso de BucketCache en file el modo con un almacenamiento de respaldo persistente tiene otro beneficio:la persistencia. Al iniciar, buscará los datos existentes en el caché y verificará su validez.

9:Según tengo entendido, hay dos componentes que indican el límite superior de este rango. El primero es un límite en la capacidad de direccionamiento de objetos JVM. La JVM puede hacer referencia a un objeto en el montón con una dirección relativa de 32 bits en lugar de la dirección nativa completa de 64 bits. Esta optimización solo es posible si el tamaño total del almacenamiento dinámico es inferior a 32 GB. Consulte Oops comprimidos para obtener más detalles. El segundo es la capacidad del recolector de basura para mantenerse al día con la cantidad de rotación de objetos en el sistema. Por lo que puedo decir, las tres fuentes de abandono de objetos son MemStoreBlockCache y operaciones de red. El primero está mitigado por MemSlab función, habilitada de forma predeterminada. El segundo está influenciado por el tamaño de su conjunto de datos frente al tamaño del caché. El tercero no se puede evitar mientras HBase haga uso de una pila de red que se base en la copia de datos.

10:Al igual que con 8, esto supone "hardware moderno". Las interacciones aquí son bastante complejas y van más allá del alcance de una sola publicación de blog.