Estos contenidos se han traducido de forma automática para su comodidad, pero Huawei Cloud no garantiza la exactitud de estos. Para consultar los contenidos originales, acceda a la versión en inglés.
Centro de ayuda/ MapReduce Service/ Descripción general del servicio/ Componentes/ HBase/ Funciones de código abierto mejoradas de HBase
Actualización más reciente 2023-04-14 GMT+08:00

Funciones de código abierto mejoradas de HBase

HIndex

HBase es una base de datos de almacenamiento distribuido del tipo Key-Value. Los datos de una tabla se ordenan en el orden alfabético basado en las claves de fila. Si consulta datos basados en una clave de fila especificada o analiza datos en la escala de una clave de fila especificada, HBase puede localizar rápidamente los datos de destino, lo que mejora la eficiencia.

Sin embargo, en la mayoría de los escenarios reales, debe consultar los datos cuyo valor de columna sea XXX. HBase proporciona la función Filter para consultar datos con un valor de columna específico. Todos los datos se analizan en el orden de las claves de fila y, a continuación, los datos se comparan con el valor de columna específico hasta que se encuentran los datos requeridos. La función Filter analiza algunos datos innecesarios para obtener los únicos datos necesarios. Por lo tanto, la función Filter no puede cumplir con los requisitos de consultas frecuentes con altos estándares de rendimiento.

HBase HIndex está diseñado para abordar estos problemas. HBase HIndex permite a HBase consultar datos basados en valores de columna específicos.

Figura 1 HIndex
  • No se admite la actualización continua para los datos de índice.
  • Restricciones de los índices combinados:
    • Todas las columnas implicadas en índices combinados deben introducirse o eliminarse en una sola mutación. De lo contrario, se producirá una incoherencia.

      Índice: IDX1=>cf1:[q1->datatype],[q2];cf2:[q2->datatype]

      Operaciones de escritura correctas:

      Put put = new Put(Bytes.toBytes("row"));
      put.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("q1"), Bytes.toBytes("valueA"));
      put.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("q2"), Bytes.toBytes("valueB"));
      put.addColumn(Bytes.toBytes("cf2"), Bytes.toBytes("q2"), Bytes.toBytes("valueC"));
      table.put(put);

      Operaciones de escritura incorrectas:

      Put put1 = new Put(Bytes.toBytes("row"));
      put1.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("q1"), Bytes.toBytes("valueA"));
      table.put(put1);
      Put put2 = new Put(Bytes.toBytes("row"));
      put2.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("q2"), Bytes.toBytes("valueB"));
      table.put(put2);
      Put put3 = new Put(Bytes.toBytes("row"));
      put3.addColumn(Bytes.toBytes("cf2"), Bytes.toBytes("q2"), Bytes.toBytes("valueC"));
      table.put(put3);
    • La consulta basada en condiciones combinadas sólo se admite cuando la columna de índice combinada contiene criterios de filtro, o no se especifican StartRow y StopRow para algunas columnas de índice.

      Índice: IDX1=>cf1:[q1->datatype],[q2];cf2:[q1->datatype]

      Operaciones de consulta correctas:

      scan 'table', {FILTER=>"SingleColumnValueFilter('cf1','q1',>=,'binary:valueA',true,true) AND SingleColumnValueFilter('cf1','q2',>=,'binary:valueB',true,true) AND SingleColumnValueFilter('cf2','q1',>=,'binary:valueC',true,true) "}
      
      scan 'table', {FILTER=>"SingleColumnValueFilter('cf1','q1',=,'binary:valueA',true,true) AND SingleColumnValueFilter('cf1','q2',>=,'binary:valueB',true,true)" }
      
      scan 'table', {FILTER=>"SingleColumnValueFilter('cf1','q1',>=,'binary:valueA',true,true) AND SingleColumnValueFilter('cf1','q2',>=,'binary:valueB',true,true) AND SingleColumnValueFilter('cf2','q1',>=,'binary:valueC',true,true)",STARTROW=>'row001',STOPROW=>'row100'}

      Operaciones de consulta incorrectas:

      scan 'table', {FILTER=>"SingleColumnValueFilter('cf1','q1',>=,'binary:valueA',true,true) AND SingleColumnValueFilter('cf1','q2',>=,'binary:valueB',true,true) AND SingleColumnValueFilter('cf2','q1',>=,'binary:valueC',true,true)  AND SingleColumnValueFilter('cf2','q2',>=,'binary:valueD',true,true)"}
      
      scan 'table', {FILTER=>"SingleColumnValueFilter('cf1','q1',=,'binary:valueA',true,true) AND SingleColumnValueFilter('cf2','q1',>=,'binary:valueC',true,true)" }
      
      scan 'table', {FILTER=>"SingleColumnValueFilter('cf1','q1',=,'binary:valueA',true,true) AND SingleColumnValueFilter('cf2','q2',>=,'binary:valueD',true,true)" }
      
      scan 'table', {FILTER=>"SingleColumnValueFilter('cf1','q1',=,'binary:valueA',true,true) AND SingleColumnValueFilter('cf1','q2',>=,'binary:valueB',true,true)" ,STARTROW=>'row001',STOPROW=>'row100' }
  • No configure explícitamente ninguna política de división para tablas con datos de índice.
  • Otras operaciones de mutación, tales como increment y append no son compatibles.
  • No se admite el índice de la columna con maxVersions mayor que 1.
  • La columna de índice de datos de una fila no se puede actualizar.

    Índice 1: IDX1=>cf1:[q1->datatype],[q2];cf2:[q1->datatype]

    Índice 2: IDX2=>cf2:[q2->datatype]

    Operaciones de actualización correctas:

    Put put1 = new Put(Bytes.toBytes("row"));
    put1.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("q1"), Bytes.toBytes("valueA"));
    put1.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("q2"), Bytes.toBytes("valueB"));
    put1.addColumn(Bytes.toBytes("cf2"), Bytes.toBytes("q1"), Bytes.toBytes("valueC"));
    put1.addColumn(Bytes.toBytes("cf2"), Bytes.toBytes("q2"), Bytes.toBytes("valueD"));
    table.put(put1);
    
    Put put2 = new Put(Bytes.toBytes("row"));
    put2.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("q3"), Bytes.toBytes("valueE"));
    put2.addColumn(Bytes.toBytes("cf2"), Bytes.toBytes("q3"), Bytes.toBytes("valueF"));
    table.put(put2);

    Operaciones de actualización incorrectas:

    Put put1 = new Put(Bytes.toBytes("row"));
    put1.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("q1"), Bytes.toBytes("valueA"));
    put1.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("q2"), Bytes.toBytes("valueB"));
    put1.addColumn(Bytes.toBytes("cf2"), Bytes.toBytes("q1"), Bytes.toBytes("valueC"));
    put1.addColumn(Bytes.toBytes("cf2"), Bytes.toBytes("q2"), Bytes.toBytes("valueD"));
    table.put(put1);
    
    Put put2 = new Put(Bytes.toBytes("row"));
    put2.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("q1"), Bytes.toBytes("valueA_new"));
    put2.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("q2"), Bytes.toBytes("valueB_new"));
    put2.addColumn(Bytes.toBytes("cf2"), Bytes.toBytes("q1"), Bytes.toBytes("valueC_new"));
    put2.addColumn(Bytes.toBytes("cf2"), Bytes.toBytes("q2"), Bytes.toBytes("valueD_new"));
    table.put(put2);
  • La tabla a la que se agrega un índice no puede contener un valor superior a 32 KB.
  • Si los datos de usuario se eliminan debido a la expiración del TTL a nivel de columna, los datos de índice correspondientes no se eliminan inmediatamente. Se eliminará en la operación de compactación principal.
  • El TTL de la familia de columnas de usuario no se puede modificar después de crear el índice.
    • Si el TTL de una familia de columnas aumenta después de crear un índice, elimine el índice y vuelva a crear uno. De lo contrario, algunos datos de índice generados se eliminarán antes de que se eliminen los datos del usuario.
    • Si el valor TTL de la familia de columnas disminuye después de crear un índice, los datos del índice se eliminarán después de eliminar los datos del usuario.
  • La consulta de índice no admite la operación inversa y los resultados de la consulta están desordenados.
  • El índice no admite la operación clone snapshot.
  • La tabla de índice debe usar HIndexWALPlayer para reproducir registros. WALPlayer no se puede usar para reproducir registros.
    hbase org.apache.hadoop.hbase.hindex.mapreduce.HIndexWALPlayer
    Usage: WALPlayer [options] <wal inputdir> <tables> [<tableMappings>]
    Read all WAL entries for <tables>.
    If no tables ("") are specific, all tables are imported.
    (Careful, even -ROOT- and hbase:meta entries will be imported in that case.)
    Otherwise <tables> is a comma separated list of tables.
    
    The WAL entries can be mapped to new set of tables via <tableMapping>.
    <tableMapping> is a command separated list of targettables.
    If specified, each table in <tables> must have a mapping.
    
    By default WALPlayer will load data directly into HBase.
    To generate HFiles for a bulk data load instead, pass the option:
      -Dwal.bulk.output=/path/for/output
      (Only one table can be specified, and no mapping is allowed!)
    Other options: (specify time range to WAL edit to consider)
      -Dwal.start.time=[date|ms]
      -Dwal.end.time=[date|ms]
    For performance also consider the following options:
      -Dmapreduce.map.speculative=false
      -Dmapreduce.reduce.speculative=false
  • Cuando se ejecuta el comando deleteall para la tabla de índices, el rendimiento es bajo.
  • La tabla de índice no admite HBCK. Para utilizar HBCK para reparar la tabla de índice, elimine primero los datos de índice.

División multipunto

Cuando crea tablas que están divididas previamente por región en HBase, es posible que no conozca la tendencia de distribución de datos, por lo que la división por región puede ser inapropiada. Después de que el sistema funcione durante un período, las regiones deben dividirse de nuevo para lograr un mejor rendimiento. Solo se pueden dividir las regiones vacías.

La función de división de regiones proporcionada con HBase divide regiones solo cuando alcanzan el umbral. Esto se llama "división de un solo punto".

Para lograr un mejor rendimiento cuando las regiones se dividen en función de los requisitos del usuario, se desarrolla la división multipunto, que también se denomina "división dinámica". Es decir, una región vacía se divide previamente en múltiples regiones para evitar el deterioro del rendimiento causado por el espacio insuficiente de la región.

Figura 2 División multipunto

Limitación de conexión

Demasiadas sesiones significan que se están ejecutando demasiadas consultas y tareas de MapReduce en HBase, lo que compromete el rendimiento de HBase e incluso provoca el rechazo del servicio. Puede configurar parámetros para limitar el número máximo de sesiones que se pueden establecer entre el cliente y el servidor HBase para lograr la protección de sobrecarga HBase.

Recuperación ante desastres mejorada

Las capacidades de recuperación ante desastres (DR) entre los clústeres activo y en espera pueden mejorar el HA de los datos de HBase. El clúster activo proporciona servicios de datos y el clúster en espera realiza copias de seguridad de los datos. Si el clúster activo es defectuoso, el clúster en espera se hace cargo de los servicios de datos. En comparación con la función de replicación de código abierto, esta función se mejora de la siguiente manera:

  1. La función de lista blanca de clústeres en espera solo es aplicable a enviar datos a una dirección IP de clúster especificada.
  2. En la versión de código abierto, la replicación se sincroniza en función de WAL, y la copia de respaldo de datos se implementa reproduciendo WAL en el clúster en espera. Para las operaciones de BulkLoad, dado que no se genera WAL, los datos no se replicarán en el clúster en espera. Al registrar las operaciones de BulkLoad en el WAL y sincronizarlas con el clúster en espera, el clúster en espera puede leer los registros de operación de BulkLoad a través de WAL y cargar HFile en el clúster activo al clúster en espera para implementar la copia de respaldo de datos.
  3. En la versión de código abierto, HBase filtra las ACL. Por lo tanto, la información de ACL no se sincronizará con el clúster en espera. Al agregar un filtro (org.apache.hadoop.hbase.replication.SystemTableWALEntryFilterAllowACL), la información de ACL se puede sincronizar con el clúster en espera. Puede configurar hbase.replication.filter.sytemWALEntryFilter para habilitar el filtro e implementar la sincronización de ACL.
  4. En cuanto a la restricción de solo lectura del clúster en espera, solo los superusuarios dentro del clúster en espera pueden modificar el HBase del clúster en espera. En otras palabras, los clientes HBase fuera del clúster en espera solo pueden leer el HBase del clúster en espera.

HBase MOB

En los escenarios de aplicación reales, es necesario almacenar datos en varios tamaños, por ejemplo, datos de imagen y documentos. Los datos cuyo tamaño sea inferior a 10 MB se pueden almacenar en HBase. HBase puede ofrecer el mejor rendimiento de lectura y escritura para datos cuyo tamaño sea inferior a 100 KB. Si el tamaño de los datos almacenados en HBase es superior a 100 KB o incluso alcanza 10 MB y se inserta el mismo número de archivos de datos, la cantidad total de datos es grande, lo que provoca compactación y división frecuentes, alto consumo de CPU, alta frecuencia de E/S de disco y bajo rendimiento.

Los datos MOB (cuyo tamaño varía de 100 KB a 10 MB) se almacenan en un sistema de archivos (por ejemplo, HDFS) en formato HFile. Las herramientas expiredMobFileCleaner y Sweeper se utilizan para gestionar HFiles y guardar la dirección y la información de tamaño sobre los HFiles en el almacén de HBase como valores. Esto reduce en gran medida la compactación y la frecuencia de división en HBase y mejora el rendimiento.

Como se muestra en Figura 3, MOB indica mobstore almacenado en HRegion. Mobstore almacena claves y valores. En el que, una clave es la clave correspondiente en HBase, y un valor es la dirección de referencia y el desplazamiento de datos almacenados en el sistema de archivos. Al leer datos, mobstore utiliza su propio escáner para leer objetos de datos clave-valor y utiliza la información de dirección y tamaño de datos en el valor para obtener datos de destino del sistema de archivos.

Figura 3 Principio de almacenamiento de datos MOB

HFS

El FileStream HBase (HFS) es un módulo de almacenamiento de archivos HBase independiente. Se utiliza en aplicaciones de capa superior MRS mediante la encapsulación de interfaces HBase y HDFS para proporcionar estas aplicaciones de capa superior con funciones tales como almacenamiento de archivos, lectura y eliminación.

En el ecosistema de Hadoop, HDFS y HBase enfrentan problemas difíciles en el almacenamiento masivo de archivos en algunos escenarios:

  • Si se almacena un gran número de archivos pequeños en HDFS, el NameNode estará bajo gran presión.
  • Algunos archivos grandes no se pueden almacenar directamente en HBase debido a las API de HBase y los mecanismos internos.

HFS está desarrollado para el almacenamiento mixto de archivos pequeños masivos y algunos archivos grandes en Hadoop. En pocas palabras, los archivos grandes pequeños (más pequeños que 10 MB) y algunos archivos grandes (más de 10 MB) deben almacenarse en tablas HBase.

Para tal escenario, HFS proporciona API de operación unificada similar a las API de función de HBase.

Múltiples RegionServers implementados en el mismo servidor

Se pueden implementar múltiples RegionServers en un nodo para mejorar la utilización de recursos de HBase.

Si solo se despliega un RegionServer, la utilización de recursos es baja debido a las siguientes razones:

  1. Un RegionServer admite un número limitado de regiones y, por lo tanto, los recursos de memoria y CPU no se pueden usar completamente.
  2. Un solo RegionServer admite un máximo de 20 TB de datos, de los cuales dos copias requieren 40 TB y tres copias requieren 60 TB. En este caso, la capacidad de 96 TB no se puede agotar.
  3. Rendimiento de escritura deficiente: se despliega un RegionServer en un servidor físico y solo existe un HLog. Solo se pueden escribir tres discos al mismo tiempo.

La utilización de recursos de HBase se puede mejorar cuando se despliegan múltiples RegionServers en el mismo servidor.

  1. Un servidor físico se puede configurar con un máximo de cinco RegionServers. El número de RegionServers desplegados en cada servidor físico se puede configurar según sea necesario.
  2. Se pueden utilizar recursos como memoria, discos y CPU.
  3. Un servidor físico admite un máximo de cinco HLogs y permite que los datos se escriban en 15 discos al mismo tiempo, lo que mejora significativamente el rendimiento de escritura.
Figura 4 Utilización mejorada de los recursos de HBase

HBase de lectura dual

En el escenario de almacenamiento HBase, es difícil garantizar una estabilidad de consulta del 99.9% debido a GC, jitter de red y sectores defectuosos de discos. La función de lectura dual de HBase se agrega para cumplir con los requisitos de fallos bajos durante la lectura aleatoria de gran volumen de datos.

La función de lectura dual de HBase se basa en la capacidad de recuperación ante desastres de los clústeres activo y en espera. La probabilidad de que los dos grupos generen fallas al mismo tiempo es mucho menor que la de un grupo. El modo de acceso simultáneo de doble clúster se utiliza para garantizar la estabilidad de la consulta. Cuando un usuario inicia una solicitud de consulta, el servicio HBase de los dos clústeres se consulta al mismo tiempo. Si el clúster activo no devuelve ningún resultado después de un período de tiempo (el tiempo de fallo máximo tolerable), se pueden utilizar los datos del clúster con la respuesta más rápida. La siguiente figura muestra el principio de funcionamiento.