Este conteúdo foi traduzido por máquina para sua conveniência e a Huawei Cloud não pode garantir que o conteúdo foi traduzido com precisão. Para exibir o conteúdo original, use o link no canto superior direito para mudar para a página em inglês.
Central de ajuda/ MapReduce Service/ Visão geral de serviço/ Componentes/ HBase/ Recursos de código aberto aprimorados do HDFS
Atualizado em 2023-05-19 GMT+08:00

Recursos de código aberto aprimorados do HDFS

HIndex

HBase é um banco de dados de armazenamento distribuído do tipo chave-valor. Os dados de uma tabela são classificados em ordem alfabética com base em chaves de linha. Se você consultar dados com base em uma chave de linha especificada ou verificar dados na escala de uma chave de linha especificada, o HBase poderá localizar rapidamente os dados de destino, aumentando a eficiência.

No entanto, na maioria dos cenários reais, você precisa consultar os dados dos quais o valor da coluna é XXX. O HBase fornece o recurso Filtro para consultar dados com um valor de coluna específico. Todos os dados são verificados na ordem das chaves de linha e, em seguida, os dados são combinados com o valor da coluna específica até que os dados necessários sejam encontrados. O recurso Filtro verifica alguns dados desnecessários para obter os únicos dados necessários. Portanto, o recurso Filtro não pode atender aos requisitos de consultas frequentes com padrões de alto desempenho.

O HBase HIndex foi projetado para resolver esses problemas. O HBase HIndex permite que o HBase consulte dados com base em valores de coluna específicos.

Figura 1 HIndex
  • Atualização contínua não é suportada para dados de índice.
  • Restrições de índices combinados:
    • Todas as colunas envolvidas em índices combinados devem ser inseridas ou excluídas em uma única mutação. Caso contrário, ocorrerá inconsistência.

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

      Operações de escrita corretas:

      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);

      Operações de escrita incorretas:

      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);
    • A consulta combinada baseada em condições é suportada somente quando a coluna de índice combinada contém critérios de filtro ou StartRow e StopRow não são especificados para algumas colunas de índice.

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

      Operações de consulta corretas:

      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'}

      Operações de consulta incorretas:

      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' }
  • Não configure explicitamente nenhuma política de divisão para tabelas com dados de índice.
  • Outras operações de mutação, como increment e append, não são suportadas.
  • O índice da coluna com maxVersions maior que 1 não é suportado.
  • A coluna de índice de dados em uma linha não pode ser atualizada.

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

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

    Operações de atualização corretas:

    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);

    Operações de atualização incorretas:

    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);
  • A tabela à qual um índice é adicionado não pode conter um valor maior que 32 KB.
  • Se os dados do usuário forem excluídos devido à expiração do TTL no nível da coluna, os dados de índice correspondentes não serão excluídos imediatamente. Eles serão excluídos na operação de compactação principal.
  • O TTL da família de colunas do usuário não pode ser modificado após a criação do índice.
    • Se o TTL de uma família de colunas aumenta depois que um índice é criado, exclua o índice e recrie um. Caso contrário, alguns dados de índice gerados serão excluídos antes que os dados do usuário sejam excluídos.
    • Se o valor do TTL da família de colunas diminuir após a criação de um índice, os dados do índice serão excluídos após os dados do usuário serem excluídos.
  • A consulta de índice não suporta a operação inversa e os resultados da consulta são desordenados.
  • O índice não suporta a operação clone snapshot.
  • A tabela de índice deve usar HIndexWALPlayer para reproduzir logs. WALPlayer não pode ser usado para reproduzir logs.
    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
  • Quando o comando deleteall é executado para a tabela de índice, o desempenho é baixo.
  • A tabela de índice não suporta HBCK. Para usar o HBCK para reparar a tabela de índice, exclua os dados de índice primeiro.

Divisão multi-ponto

Quando você cria tabelas que são pré-divididas por região no HBase, talvez você não saiba a tendência de distribuição de dados, portanto, a divisão por região pode ser inadequada. Depois que o sistema funciona por um período, as regiões precisam ser divididas novamente para obter um melhor desempenho. Somente regiões vazias podem ser divididas.

A função de divisão de região fornecida com o HBase divide regiões somente quando elas atingem o limite. Isso é chamado de "divisão de ponto único".

Para alcançar um melhor desempenho quando as regiões são divididas com base nos requisitos do usuário, é desenvolvida a divisão multi-ponto, que também é chamada de "divisão dinâmica". Ou seja, uma região vazia é pré-dividida em várias regiões para evitar a deterioração do desempenho causada por espaço de região insuficiente.

Figura 2 Divisão multi-ponto

Limitação de conexão

Muitas sessões significam que muitas consultas e tarefas de MapReduce estão sendo executadas no HBase, o que compromete o desempenho do HBase e até causa rejeição de serviço. Você pode configurar parâmetros para limitar o número máximo de sessões que podem ser estabelecidas entre o cliente e o servidor HBase para obter a proteção contra sobrecarga do HBase.

Recuperação de desastres aprimorada

Os recursos de recuperação de desastres (DR) entre os clusters ativo e em espera podem aprimorar o HA dos dados do HBase. O cluster ativo fornece serviços de dados e o cluster em espera faz backup dos dados. Se o cluster ativo estiver com defeito, o cluster em espera assumirá os serviços de dados. Em comparação com a função de replicação de código aberto, essa função é aprimorada da seguinte maneira:

  1. A função de lista branca de cluster em espera só é aplicável ao envio de dados para um endereço IP de cluster especificado.
  2. Na versão de código aberto, a replicação é sincronizada com base no WAL e o backup de dados é implementado repetindo o WAL no cluster em espera. Para operações de BulkLoad, como nenhum WAL é gerado, os dados não serão replicados para o cluster em espera. Ao registrar as operações do BulkLoad no WAL e sincronizá-las com o cluster em espera, o cluster em espera pode ler registros de operação do BulkLoad por meio do WAL e carregar o HFile no cluster ativo para implementar o backup de dados.
  3. Na versão de código aberto, o HBase filtra as ACLs. Portanto, as informações de ACL não serão sincronizadas com o cluster em espera. Adicionando um filtro (org.apache.hadoop.hbase.replication.SystemTableWALEntryFilterAllowACL), as informações da ACL podem ser sincronizadas com o cluster de espera. Você pode configurar hbase.replication.filter.sytemWALEntryFilter para habilitar o filtro e implementar a sincronização da ACL.
  4. Quanto à restrição somente leitura do cluster em espera, somente superusuários dentro do cluster em espera podem modificar o HBase do cluster em espera. Em outras palavras, os clientes HBase fora do cluster em espera só podem ler o HBase do cluster em espera.

HBase MOB

Nos cenários reais da aplicação, os dados em vários tamanhos precisam ser armazenados, por exemplo, dados de imagem e documentos. Dados cujo tamanho seja menor que 10 MB podem ser armazenados no HBase. O HBase pode produzir o melhor desempenho de leitura e gravação para dados cujo tamanho é menor que 100 KB. Se o tamanho dos dados armazenados no HBase for maior que 100 KB ou mesmo atingir 10 MB e o mesmo número de arquivos de dados for inserido, a quantidade total de dados será grande, causando compactação e divisão frequentes, alto consumo de CPU, alta frequência de I/O do disco e baixo desempenho.

Os dados MOB (cujo tamanho varia de 100 KB a 10 MB) são armazenados em um sistema de arquivos (por exemplo, HDFS) no formato HFile. As ferramentas expiradasMobFileCleaner e Sweeper são usadas para gerenciar HFiles e salvar as informações de endereço e tamanho sobre os HFiles no armazenamento do HBase como valores. Isso diminui muito a compactação e a frequência de divisão no HBase e melhora o desempenho.

Como mostrado em Figura 3, MOB indica mobstore armazenado em HRegion. O Mobstore armazena chaves e valores. Em que, uma chave é a chave correspondente no HBase e um valor é o endereço de referência e o deslocamento de dados armazenados no sistema de arquivos. Ao ler dados, o mobstore usa seu próprio verificador para ler objetos de dados chave-valor e usa as informações de endereço e tamanho de dados no valor para obter dados de destino do sistema de arquivos.

Figura 3 Princípio de armazenamento de dados MOB

HFS

HBase FileStream (HFS) é um módulo independente de armazenamento de arquivos HBase. Ele é usado em aplicações de camada superior do MRS encapsulando interfaces HBase e HDFS para fornecer essas aplicações de camada superior com funções como armazenamento de arquivos, leitura e exclusão.

No ecossistema Hadoop, o HDFS e o HBase enfrentam problemas difíceis no armazenamento de arquivos em massa em alguns cenários:

  • Se um grande número de arquivos pequenos forem armazenados no HDFS, o NameNode estará sob grande pressão.
  • Alguns arquivos grandes não podem ser armazenados diretamente no HBase devido às APIs e mecanismos internos do HBase.

O HFS é desenvolvido para o armazenamento misto de pequenos arquivos massivos e alguns arquivos grandes no Hadoop. Simplificando, grandes arquivos pequenos (menores que 10 MB) e alguns arquivos grandes (maiores que 10 MB) precisam ser armazenados em tabelas do HBase.

Para esse cenário, o HFS fornece APIs de operação unificadas semelhantes às APIs de função do HBase.

Vários RegionServers implementados no mesmo servidor

Vários RegionServers podem ser implementados em um nó para melhorar a utilização de recursos do HBase.

Se apenas um RegionServer for implementado, a utilização de recursos será baixa devido aos seguintes motivos:

  1. Um RegionServer oferece suporte a um número limitado de regiões e, portanto, os recursos de memória e CPU não podem ser totalmente usados.
  2. Um único RegionServer oferece suporte a no máximo 20 TB de dados, dos quais duas cópias exigem 40 TB e três cópias exigem 60 TB. Neste caso, a capacidade de 96 TB não pode ser utilizada.
  3. Desempenho de gravação ruim: um RegionServer é implementado em um servidor físico e existe apenas um HLog. Apenas três discos podem ser gravados ao mesmo tempo.

A utilização de recursos do HBase pode ser aprimorada quando vários RegionServers são implementados no mesmo servidor.

  1. Um servidor físico pode ser configurado com um máximo de cinco RegionServers. O número de RegionServers implementados em cada servidor físico pode ser configurado conforme necessário.
  2. Recursos como memória, discos e CPUs podem ser totalmente utilizados.
  3. Um servidor físico suporta um máximo de cinco HLogs e permite que os dados sejam gravados em 15 discos ao mesmo tempo, melhorando significativamente o desempenho de gravação.
Figura 4 Utilização aprimorada de recursos do HBase

Leitura dupla do HBase

No cenário de armazenamento HBase, é difícil garantir 99,9% de estabilidade de consulta devido a GC, tremulação de rede e setores defeituosos de discos. O recurso de leitura dupla do HBase é adicionado para atender aos requisitos de falhas baixas durante a leitura aleatória de grande volume de dados.

O recurso de leitura dupla do HBase baseia-se na capacidade de DR dos clusters ativos e em espera. A probabilidade de que os dois aglomerados gerem falhas ao mesmo tempo, é muito menor do que a de um aglomerado. O modo de acesso simultâneo de cluster duplo é usado para garantir a estabilidade da consulta. Quando um usuário inicia uma solicitação de consulta, o serviço HBase dos dois clusters é consultado ao mesmo tempo. Se o cluster ativo não retornar nenhum resultado após um período de tempo (o tempo máximo de falha tolerável), os dados do cluster com a resposta mais rápida podem ser usados. A figura a seguir mostra o princípio de funcionamento.