HBase
 sql >> Base de Dados >  >> NoSQL >> HBase

HBase BlockCache 101

Esta postagem do blog foi publicada no Hortonworks.com antes da fusão com a Cloudera. Alguns links, recursos ou referências podem não ser mais precisos.

Esta postagem do blog apareceu originalmente aqui e é reproduzida na íntegra aqui.

O HBase é um banco de dados distribuído construído em torno dos conceitos principais de um log de gravação ordenado e uma árvore de mesclagem estruturada em log. Como em qualquer banco de dados, a E/S otimizada é uma preocupação crítica para o HBase. Quando possível, a prioridade é não realizar nenhuma E/S. Isso significa que a utilização da memória e as estruturas de cache são de extrema importância. Para tanto, o HBase mantém duas estruturas de cache:o “armazenamento de memória” e o “cache de bloco”. Armazenamento de memória, implementado como o MemStore , acumula edições de dados à medida que são recebidas, armazenando-as na memória (1). O cache de bloco, uma implementação do BlockCache interface, mantém os blocos de dados residentes na memória depois de lidos.

MemStore é importante para acessar edições recentes. Sem a MemStore , acessar esses dados conforme foram gravados no registro de gravação exigiria a leitura e desserialização de entradas de volta desse arquivo, pelo menos um O(n) Operação. Em vez disso, MemStore mantém uma estrutura de skiplist, que possui um O(log n) custo de acesso e não requer E/S de disco. A MemStore contém apenas uma pequena parte dos dados armazenados no HBase, no entanto.

Serviço de leituras do BlockCache é o principal mecanismo pelo qual o HBase é capaz de servir leituras aleatórias com latência de milissegundos. Quando um bloco de dados é lido do HDFS, ele é armazenado em cache no BlockCache . Leituras subsequentes de dados vizinhos – dados do mesmo bloco – não sofrem a penalidade de E/S de recuperar novamente esses dados do disco (2). É o BlockCache esse será o foco restante deste post.

Blocos para armazenar em cache


Antes de entender o BlockCache , ajuda a entender o que é exatamente um “bloco” HBase. No contexto HBase, um bloco é uma única unidade de E/S. Ao gravar dados em um HFile, o bloco é a menor unidade de dados gravados. Da mesma forma, um único bloco é a menor quantidade de dados que o HBase pode ler de volta de um HFile. Tenha cuidado para não confundir um bloco HBase com um bloco HDFS ou com os blocos do sistema de arquivos subjacente – todos são diferentes (3).

Os blocos HBase vêm em 4 variedades: DATAMETAINDEXBLOOM .

DATA blocos armazenam dados do usuário. Quando o BLOCKSIZE for especificado para uma família de colunas, é uma dica para esse tipo de bloco. Atenção, é apenas uma dica. Ao liberar o MemStore , o HBase fará o possível para honrar essa diretriz. Após cada Cell é gravado, o escritor verifica se o valor gravado é>=o destino BLOCKSIZE . Nesse caso, fechará o bloco atual e iniciará o próximo (4).

INDEXBLOOM os blocos servem ao mesmo objetivo; ambos são usados ​​para acelerar o caminho de leitura. INDEX os blocos fornecem um índice sobre a Cell s contidos em DATA blocos. BLOOM os blocos contêm um filtro Bloom sobre os mesmos dados. O índice permite que o leitor saiba rapidamente onde uma Cell deve ser armazenado. O filtro informa ao leitor quando uma Cell está definitivamente ausente dos dados.

Por fim, META os blocos armazenam informações sobre o próprio HFile e outras informações diversas – metadados, como você pode esperar. Uma visão geral mais abrangente dos formatos HFile e as funções de vários tipos de bloco é fornecida em Apache HBase I/O – HFile.

HBase BlockCache e suas implementações


Há um único BlockCache instância em um servidor de região, o que significa que todos os dados de todas as regiões hospedadas por esse servidor compartilham o mesmo pool de cache (5). O BlockCache é instanciado na inicialização do servidor de região e é retido durante toda a vida útil do processo. Tradicionalmente, o HBase fornecia apenas um único BlockCache implementação:o LruBlockCache . A versão 0.92 introduziu a primeira alternativa no HBASE-4027:o SlabCache . O HBase 0.96 introduziu outra opção via HBASE-7404, chamada BucketCache .

A principal diferença entre o testado e comprovado LruBlockCache e essas alternativas são a maneira como gerenciam a memória. Especificamente, LruBlockCache é uma estrutura de dados que reside inteiramente no heap da JVM, enquanto os outros dois podem aproveitar a memória de fora do heap da JVM. Essa é uma distinção importante porque a memória heap da JVM é gerenciada pelo JVM Garbage Collector, enquanto as outras não são. Nos casos de SlabCacheBucketCache , a ideia é reduzir a pressão do GC experimentada pelo processo do servidor de região reduzindo o número de objetos retidos no heap.

LruBlockCache


Esta é a implementação padrão. Os blocos de dados são armazenados em cache no heap da JVM usando essa implementação. Ele é subdividido em três áreas:acesso único, acesso múltiplo e na memória. As áreas são dimensionadas em 25%, 50%, 25% do total BlockCache tamanho, respectivamente (6). Um bloco lido inicialmente do HDFS é preenchido na área de acesso único. Acessos consecutivos promovem esse bloco na área de multiacesso. A área na memória é reservada para blocos carregados de famílias de colunas sinalizadas como IN_MEMORY . Independentemente da área, os blocos antigos são despejados para dar espaço a novos blocos usando um algoritmo de uso menos recente, daí o “Lru” em “LruBlockCache”.

SlabCache


Essa implementação aloca áreas de memória fora do heap da JVM usando DirectByteBuffer s. Essas áreas fornecem o corpo deste BlockCache . A área precisa na qual um bloco específico será colocado é baseada no tamanho do bloco. Por padrão, duas áreas são alocadas, consumindo 80% e 20% do tamanho total do cache off-heap configurado, respectivamente. O primeiro é usado para armazenar em cache blocos que são aproximadamente o tamanho do bloco de destino (7). O último contém blocos que são aproximadamente 2x o tamanho do bloco de destino. Um bloco é colocado na menor área onde ele pode caber. Se o cache encontrar um bloco maior do que cabe em qualquer área, esse bloco não será armazenado em cache. Como LruBlockCache , a remoção de bloco é gerenciada usando um algoritmo LRU.

BucketCache


Essa implementação pode ser configurada para operar em um dos três modos diferentes: heapoffheapfile . Independentemente do modo de operação, o BucketCache gerencia áreas de memória chamadas “buckets” para armazenar blocos em cache. Cada bucket é criado com um tamanho de bloco de destino. O heap a implementação cria esses buckets no heap da JVM; offheap implementação usa DirectByteByffers para gerenciar buckets fora do heap da JVM; file mode espera um caminho para um arquivo no sistema de arquivos em que os buckets são criados. file O modo destina-se ao uso com um armazenamento de backup de baixa latência – um sistema de arquivos na memória ou talvez um arquivo em armazenamento SSD (8). Independentemente do modo, BucketCache cria 14 baldes de tamanhos diferentes. Ele usa a frequência de acesso em bloco para informar a utilização, assim como o LruBlockCache , e tem a mesma divisão de acesso único, multiacesso e na memória de 25%, 50%, 25%. Também como o cache padrão, o despejo de bloco é gerenciado usando um algoritmo LRU.

Cache de vários níveis


Tanto o SlabCacheBucketCache são projetados para serem usados ​​como parte de uma estratégia de cache multinível. Assim, uma parte do total de BlockCache o tamanho é atribuído a um LruBlockCache instância. Essa instância atua como o cache de primeiro nível, “L1”, enquanto a outra instância de cache é tratada como o cache de segundo nível, “L2”. No entanto, a interação entre LruBlockCacheSlabCache é diferente de como o LruBlockCache e o BucketCache interagir.

SlabCache estratégia, chamada DoubleBlockCache , é sempre armazenar blocos em cache nos caches L1 e L2. Os dois níveis de cache operam independentemente:ambos são verificados ao recuperar um bloco e cada um despeja blocos sem considerar o outro. O BucketCache estratégia, chamada CombinedBlockCache , usa o cache L1 exclusivamente para blocos Bloom e Index. Os blocos de dados são enviados diretamente para o cache L2. No caso de remoção do bloco L1, em vez de ser totalmente descartado, esse bloco é rebaixado para o cache L2.

Qual ​​escolher?


Há dois motivos para considerar a ativação de uma das alternativas BlockCache implementações. A primeira é simplesmente a quantidade de RAM que você pode dedicar ao servidor da região. A sabedoria da comunidade reconhece que o limite superior do heap da JVM, no que diz respeito ao servidor da região, está entre 14 GB e 31 GB (9). O limite preciso geralmente depende de uma combinação de perfil de hardware, configuração de cluster, formato das tabelas de dados e padrões de acesso a aplicativos. Você saberá que entrou na zona de perigo quando o GC pausar e RegionTooBusyException s começar a inundar seus logs.

O outro momento para considerar um cache alternativo é quando a latência de resposta realmente assuntos. Manter o heap em torno de 8 a 12 GB permite que o coletor CMS funcione muito bem (10), o que tem um impacto mensurável no 99º percentil dos tempos de resposta. Dada essa restrição, as únicas opções são explorar um coletor de lixo alternativo ou usar uma dessas implementações fora do heap para dar uma volta.

Esta segunda opção é exatamente o que eu fiz. Na minha próxima postagem, compartilharei alguns resultados de experimentos não científicos, mas informativos, nos quais comparo os tempos de resposta para diferentes BlockCache implementações.

Como sempre, fique atento e continue com o HBase!

1:A MemStore acumula edições de dados à medida que são recebidas, armazenando-as na memória. Isso serve a dois propósitos:aumenta a quantidade total de dados gravados no disco em uma única operação e retém essas alterações recentes na memória para acesso subsequente na forma de leituras de baixa latência. O primeiro é importante, pois mantém os blocos de gravação do HBase praticamente sincronizados com os tamanhos de bloco do HDFS, alinhando os padrões de acesso do HBase com o armazenamento HDFS subjacente. Este último é autoexplicativo, facilitando as solicitações de leitura de dados gravados recentemente. Vale ressaltar que essa estrutura não está envolvida na durabilidade dos dados. As edições também são gravadas no registro de gravação ordenado, o HLog , que envolve uma operação de acréscimo do HDFS em um intervalo configurável, geralmente imediato.

2:Reler os dados do sistema de arquivos local é o melhor cenário. Afinal, o HDFS é um sistema de arquivos distribuído, então o pior caso requer a leitura desse bloco pela rede. O HBase faz o possível para manter a localidade dos dados. Esses dois artigos fornecem uma visão detalhada do que a localidade de dados significa para o HBase e como ele é gerenciado.

3:Os blocos do sistema de arquivos, HDFS e HBase são todos diferentes, mas relacionados. O subsistema de E/S moderno é formado por muitas camadas de abstração sobre abstração. O núcleo dessa abstração é o conceito de uma única unidade de dados, chamada de “bloco”. Portanto, todas as três camadas de armazenamento definem seu próprio bloco, cada uma com seu próprio tamanho. Em geral, um tamanho de bloco maior significa maior taxa de transferência de acesso sequencial. Um tamanho de bloco menor facilita o acesso aleatório mais rápido.

4:Colocar o BLOCKSIZE verificar depois que os dados são gravados tem duas ramificações. Uma única Cell é a menor unidade de dados gravada em um DATA quadra. Também significa uma Cell não pode abranger vários blocos.

5:é diferente da MemStore , para o qual há uma instância separada para cada região hospedada pelo servidor da região.

6:Até muito recentemente, essas partições de memória eram definidas estaticamente; não havia como substituir a divisão 25/50/25. Um determinado segmento, a área de multiacesso, por exemplo, poderia crescer mais do que sua cota de 50%, desde que as demais áreas fossem subutilizadas. O aumento da utilização nas outras áreas despejará as entradas da área de multiacesso até que o saldo de 25/50/25 seja atingido. O operador não pôde alterar esses tamanhos padrão. O HBASE-10263, fornecido no HBase 0.98.0, apresenta parâmetros de configuração para esses tamanhos. O comportamento flexível é mantido.

7:O negócio “aproximadamente” é permitir algum espaço de manobra no tamanho dos blocos. O tamanho do bloco HBase é um destino ou dica aproximado, não uma restrição estritamente aplicada. O tamanho exato de qualquer bloco de dados específico dependerá do tamanho do bloco de destino e do tamanho da Cell valores nele contidos. A dica de tamanho de bloco é especificada como o tamanho de bloco padrão de 64kb.

8:como usar o BucketCache em file O modo com um armazenamento de suporte persistente tem outro benefício:persistência. Na inicialização, ele procurará os dados existentes no cache e verificará sua validade.

9:Pelo que entendi, há dois componentes aconselhando o limite superior desse intervalo. O primeiro é um limite na capacidade de endereçamento do objeto JVM. A JVM pode fazer referência a um objeto no heap com um endereço relativo de 32 bits em vez do endereço nativo completo de 64 bits. Essa otimização só é possível se o tamanho total do heap for menor que 32 GB. Consulte Oops compactados para mais detalhes. A segunda é a capacidade do coletor de lixo de acompanhar a quantidade de rotatividade de objetos no sistema. Pelo que posso dizer, as três fontes de rotatividade de objetos são MemStoreBlockCache e operações de rede. A primeira é atenuada pelo MemSlab recurso, ativado por padrão. A segunda é influenciada pelo tamanho do seu conjunto de dados versus o tamanho do cache. A terceira não pode ser evitada enquanto o HBase fizer uso de uma pilha de rede que depende da cópia de dados.

10:Assim como no 8, isso pressupõe "hardware moderno". As interações aqui são bastante complexas e muito além do escopo de uma única postagem no blog.