Introdução
Apache HBase é o gerenciador de armazenamento de código aberto, distribuído e com versão do Hadoop, adequado para aleatório , leitura/gravação em tempo real Acesso.
Espera espera? acesso aleatório de leitura/gravação em tempo real?
Como isso é possível? O Hadoop não é apenas um sistema de processamento em lote sequencial de leitura/gravação?
Sim, estamos falando da mesma coisa e, nos próximos parágrafos, vou explicar a você como o HBase obtém a E/S aleatória, como armazena dados e a evolução do formato HFile do HBase.
Formatos de arquivo de E/S do Apache Hadoop
O Hadoop vem com um formato de arquivo SequenceFile[1] que você pode usar para anexar seus pares de chave/valor, mas devido à capacidade somente de anexação de hdfs, o formato de arquivo não pode permitir a modificação ou remoção de um valor inserido. A única operação permitida é anexar e, se você quiser pesquisar uma chave especificada, deverá ler o arquivo até encontrar sua chave.
Como você pode ver, você é forçado a seguir o padrão sequencial de leitura/gravação... mas como é possível construir um sistema de acesso de leitura/gravação aleatório e de baixa latência como o HBase em cima disso?
Para ajudá-lo a resolver esse problema, o Hadoop possui outro formato de arquivo, chamado MapFile[1], uma extensão do SequenceFile. O MapFile, na verdade, é um diretório que contém dois SequenceFiles:o arquivo de dados “/data” e o arquivo de índice “/index”. O MapFile permite anexar pares de chave/valor classificados e cada N chaves (onde N é um intervalo configurável) ele armazena a chave e o deslocamento no índice. Isso permite uma pesquisa bastante rápida, pois em vez de varrer todos os registros, você varre o índice que tem menos entradas. Depois de encontrar seu bloco, você pode pular para o arquivo de dados real.
MapFile é bom porque você pode pesquisar pares de chave/valor rapidamente, mas ainda há dois problemas:
- Como posso excluir ou substituir uma chave/valor?
- Quando minha entrada não está classificada, não consigo usar MapFile.
HBase &MapFile
A Chave HBase consiste em:a chave de linha, família de colunas, qualificador de coluna, carimbo de data/hora e um tipo.
Para resolver o problema de deletar pares chave/valor, a ideia é usar o campo “tipo” para marcar a chave como deletada (marcadores de tombstone). Resolver o problema de substituir pares chave/valor é apenas uma questão de escolher o carimbo de data/hora posterior (o valor correto está próximo ao final do arquivo, anexar significa apenas que a última inserção está próxima ao final).
Para resolver o problema da chave “não ordenada”, mantemos os últimos valores-chave adicionados na memória. Quando você atingir um limite, o HBase o libera em um MapFile. Dessa forma, você acaba adicionando chaves/valores classificados a um MapFile.
O HBase faz exatamente isso[2]:quando você adiciona um valor com table.put(), sua chave/valor é adicionado ao MemStore (sob o capô MemStore é um ConcurrentSkipListMap classificado). Quando o limite por memstore (hbase.hregion.memstore.flush.size) é atingido ou o RegionServer está usando muita memória para memstores (hbase.regionserver.global.memstore.upperLimit), os dados são liberados no disco como um novo MapFile .
O resultado de cada flush é um novo MapFile, e isso significa que para encontrar uma chave é preciso pesquisar em mais de um arquivo. Isso consome mais recursos e é potencialmente mais lento.
Cada vez que um get ou scan é emitido, o HBase varre cada arquivo para encontrar o resultado, para evitar pular muitos arquivos, há um thread que detectará quando você atingir um certo número de arquivos (hbase.hstore.compaction .máx.). Em seguida, ele tenta mesclá-los em um processo chamado compactação, que basicamente cria um novo arquivo grande como resultado da mesclagem de arquivos.
O HBase possui dois tipos de compactação:uma chamada “compactação menor” que apenas mescla dois ou mais arquivos pequenos em um, e a outra chamada “compactação maior” que pega todos os arquivos da região, os mescla e realiza alguma limpeza. Em uma grande compactação, as chaves/valores excluídos são removidos, esse novo arquivo não contém os marcadores de tombstone e todas as chaves/valores duplicados (operações de valor de substituição) são removidas.
Até a versão 0.20, o HBase usava o formato MapFile para armazenar os dados, mas na versão 0.20 um novo MapFile específico do HBase foi introduzido (HBASE-61).
Arquivo v1
No HBase 0.20, MapFile é substituído por HFile:uma implementação de arquivo de mapa específico para HBase. A ideia é bastante semelhante ao MapFile, mas adiciona mais recursos do que apenas um arquivo de chave/valor simples. Recursos como suporte a metadados e índice agora são mantidos no mesmo arquivo.
Os blocos de dados contêm a chave/valores reais como um MapFile. Para cada “operação de fechamento de bloco” a primeira chave é adicionada ao índice, e o índice é escrito em HFile close.
O formato HFile também adiciona dois tipos de blocos “metadados” extras:Meta e FileInfo. Esses dois blocos de chave/valor são gravados no fechamento do arquivo.
O bloco Meta é projetado para manter uma grande quantidade de dados com sua chave como uma String, enquanto FileInfo é um Map simples preferido para pequenas informações com chaves e valores que são ambos byte-array. O StoreFile do Regionserver usa Meta-Blocks para armazenar um Bloom Filter e FileInfo para Max SequenceId, chave de compactação principal e informações de intervalo de tempo. Esta informação é útil para evitar a leitura do arquivo se não houver chance de que a chave esteja presente (Bloom Filter), se o arquivo for muito antigo (Max SequenceId) ou se o arquivo for muito novo (Timerange) para conter o que estamos procurando por.
Arquivo v2
No HBase 0.92, o formato HFile foi um pouco alterado (HBASE-3857) para melhorar o desempenho quando grandes quantidades de dados são armazenadas. Um dos principais problemas com o HFile v1 é que você precisa carregar todos os índices monolíticos e filtros Bloom grandes na memória e, para resolver esse problema, a v2 introduz índices multinível e um filtro Bloom em nível de bloco. Como resultado, o HFile v2 apresenta velocidade, memória e uso de cache aprimorados.
A principal característica desta v2 são os “blocos inline”, a ideia é quebrar o índice e Bloom Filter por bloco, ao invés de ter todo o índice e Bloom Filter de todo o arquivo na memória. Desta forma, você pode manter em ram apenas o que você precisa.
Como o índice é movido para o nível de bloco, você tem um índice multinível, o que significa que cada bloco tem seu próprio índice (folha-índice). A última chave de cada bloco é mantida para criar o intermediário/índice que torna o índice multinível b+tree.
O cabeçalho do bloco agora contém algumas informações:O campo “Block Magic” foi substituído pelo campo “Block Type” que descreve o conteúdo do bloco “Data”, Leaf-Index, Bloom, Metadata, Root-Index, etc. Também três campos (tamanho compactado/descompactado e bloco anterior de deslocamento) foram adicionados para permitir buscas rápidas para trás e para frente.
Codificações de bloco de dados
Como as chaves são classificadas e geralmente muito semelhantes, é possível projetar uma compactação melhor do que um algoritmo de uso geral pode fazer.
O HBASE-4218 tentou resolver esse problema, e no HBase 0.94 você pode escolher entre dois algoritmos diferentes:Prefix e Diff Encoding.
A ideia principal da Codificação de prefixo é armazenar o prefixo comum apenas uma vez, pois as linhas são classificadas e o início é normalmente o mesmo.
A Codificação Diff leva esse conceito ainda mais longe. Em vez de considerar a chave como uma sequência opaca de bytes, o Diff Encoder divide cada campo de chave para compactar cada parte de uma maneira melhor. Sendo assim, a família de colunas é armazenada uma vez. Se o comprimento da chave, o comprimento do valor e o tipo forem iguais à linha anterior, o campo será omitido. Além disso, para aumentar a compactação, o carimbo de data/hora é armazenado como um Diff do anterior.
Observe que esse recurso está desativado por padrão, pois a gravação e a digitalização são mais lentas, mas mais dados são armazenados em cache. Para habilitar este recurso, você pode definir DATA_BLOCK_ENCODING =PREFIX | DIFF | FAST_DIFF nas informações da tabela.
Arquivo v3
HBASE-5313 contém uma proposta para reestruturar o layout HFile para melhorar a compressão:
- Agrupe todas as chaves no início do bloco e todos os valores juntos no final do bloco. Dessa forma, você pode usar dois algoritmos diferentes para compactar a chave e os valores.
- Comprima os carimbos de data/hora usando o XOR com o primeiro valor e use VInt em vez de longo.
Além disso, um formato colunar ou uma codificação colunar está sob investigação, dê uma olhada no AVRO-806 para um formato de arquivo colunar por Doug Cutting.
Como você pode ver, a tendência na evolução é estar mais ciente sobre o que o arquivo contém, para obter melhor compactação ou melhor reconhecimento de localização que se traduz em menos dados para gravar/ler do disco. Menos E/S significa mais velocidade!
[1] https://clouderatemp.wpengine.com/blog/2011/01/hadoop-io-sequence-map-set-array-bloommap-files/
[2] https://clouderatemp.wpengine. com/blog/2012/06/hbase-write-path/