6.1 Quem inventou o HBase e por quê
Nesta palestra falaremos sobre uma ferramenta tão maravilhosa como o Hbase, que recentemente ganhou grande popularidade: por exemplo, o Facebook o utiliza como base de seu sistema de mensagens, e isso já diz muito.
A palestra falará sobre o conceito de Big Table e sua implementação gratuita, características de trabalho e diferenças tanto de bancos de dados relacionais clássicos (como MySQL e Oracle) quanto de armazenamentos de valor-chave como Redis, Aerospike e memcached. Como de costume, vamos começar com a história do problema. Como muitos outros projetos de BigData, o Hbase nasceu de um conceito desenvolvido pelo Google. Os princípios por trás do Hbase foram descritos no artigo Bigtable: A Distributed Storage System for Structured Data .
Como discutimos em aulas anteriores, os arquivos comuns são bastante adequados para o processamento de dados em lote usando o paradigma MapReduce. Por outro lado, as informações armazenadas em arquivos são bastante inconvenientes para atualizar; Os arquivos também são privados da possibilidade de acesso aleatório. Para um trabalho rápido e conveniente com acesso aleatório, existe uma classe de sistemas nosql, como armazenamento de valor-chave, como Aerospike, Redis, Couchbase, Memcached. No entanto, o processamento em lote é geralmente muito inconveniente nesses sistemas. O Hbase é uma tentativa de combinar a conveniência do processamento em lote com a conveniência da atualização e do acesso aleatório.
![](https://cdn.codegym.cc/images/article/54d64a29-4642-4c59-8211-2b143dfcd48a/512.jpeg)
6.2 Modelo de dados
O HBase é um banco de dados de chave-valor distribuído, orientado a colunas e multiversão.
- Os dados são organizados em tabelas indexadas por uma chave primária chamada RowKey no Hbase.
- Para cada chave RowKey, um conjunto ilimitado de atributos (ou colunas) pode ser armazenado.
- As colunas são organizadas em grupos de colunas chamados Família de Colunas. Como regra, as colunas que têm o mesmo padrão de uso e armazenamento são combinadas em uma família de colunas.
- Para cada atributo, várias versões diferentes podem ser armazenadas. Versões diferentes têm carimbo de data/hora diferente.
Os registros são armazenados fisicamente na ordem de classificação RowKey. Neste caso, os dados correspondentes a diferentes famílias de colunas são armazenados separadamente, o que permite, se necessário, ler dados apenas da família de colunas desejada.
Quando um determinado atributo é excluído, ele não é excluído fisicamente imediatamente, mas apenas marcado com um sinalizador de lápide especial. A exclusão física dos dados ocorrerá posteriormente, quando a operação de Compactação Principal for executada.
Os atributos pertencentes ao mesmo grupo de colunas e correspondentes à mesma chave são armazenados fisicamente como uma lista classificada. Qualquer atributo pode estar ausente ou presente para cada chave e, se o atributo estiver ausente, isso não causará a sobrecarga de armazenar valores vazios.
Os nomes da lista e do grupo de colunas são fixos e têm um layout claro. No nível do grupo de colunas, parâmetros como tempo de vida (TTL) e o número máximo de versões armazenadas são definidos. Se a diferença entre o registro de data e hora de uma versão específica e a hora atual for maior que TTL, a entrada será marcada para exclusão. Se o número de versões de um determinado atributo exceder o número máximo de versões, o registro também será marcado para exclusão.
![](https://cdn.codegym.cc/images/article/38a31201-f909-4a45-9792-a5d72225f060/512.jpeg)
O modelo de dados Hbase pode ser lembrado como uma correspondência de valor-chave:
<table, RowKey, Column Family, Column, timestamp> -> Value
6.3 Operações suportadas
A lista de operações suportadas no hbase é bastante simples. 4 operações principais são suportadas:
- Put : adiciona uma nova entrada ao hbase. O carimbo de data/hora desta entrada pode ser definido manualmente, caso contrário, será definido automaticamente para a hora atual.
- Get : Obtenha dados para uma RowKey específica. Você pode especificar a família de colunas da qual retiraremos os dados e o número de versões que desejamos ler.
- Digitalizar : lê os registros um por um. Você pode especificar o registro do qual iniciamos a leitura, o registro para o qual ler, o número de registros a serem lidos, a Família de Colunas a partir da qual a leitura será realizada e o número máximo de versões para cada registro.
- Excluir : marque uma versão específica para exclusão. Não haverá exclusão física, será adiada até a próxima Grande Compactação (veja abaixo).
6.4 Arquitetura
O HBase é um banco de dados distribuído que pode ser executado em dezenas ou centenas de servidores físicos, garantindo operação ininterrupta mesmo que alguns deles falhem. Portanto, a arquitetura do HBase é bastante complexa comparada aos bancos de dados relacionais clássicos.
![](https://cdn.codegym.cc/images/article/5bae086c-b213-47db-be33-a23d854a1543/1024.jpeg)
O HBase usa dois processos principais para seu trabalho:
1. Region Server - Serve uma ou mais regiões. Uma região é um intervalo de registros correspondente a um intervalo específico de RowKeys consecutivos. Cada região contém:
- Persistent Storage é o principal armazenamento de dados no HBase. Os dados são armazenados fisicamente no HDFS, em um formato especial HFile. Os dados em HFile são armazenados na ordem de classificação RowKey. Um par (região, família de colunas) corresponde a pelo menos um HFIle.
- MemStore - buffer de gravação. Como os dados são armazenados em HFile d em ordem de classificação, é muito caro atualizar o HFile por registro. Em vez disso, ao gravar, os dados entram em uma área especial da memória MemStore, onde se acumulam por algum tempo. Quando o MemStore é preenchido com algum valor crítico, os dados são gravados em um novo HFile.
- BlockCache - cache para leitura. Permite economizar significativamente tempo em dados que são lidos com frequência.
- Registro Antecipado de Gravação (WAL) . Como os dados são gravados no memstore, há algum risco de perda de dados devido a uma falha. Para evitar que isso aconteça, todas as operações antes da implementação real das manipulações caem em um arquivo de log especial. Isso permite que você recupere dados após qualquer falha.
2. Servidor mestre - o servidor principal no cluster HBase. O Master gerencia a distribuição de regiões entre Region Servers, mantém um registro de regiões, gerencia o lançamento de tarefas regulares e faz outros trabalhos úteis.
Para coordenar as ações entre os serviços, o HBase usa o Apache ZooKeeper, um serviço especial projetado para gerenciar configurações e sincronizar serviços.
Quando a quantidade de dados na região aumenta e atinge um determinado tamanho, o Hbase começa a dividir, uma operação que divide a região por 2. Para evitar divisões constantes de regiões, você pode pré-definir os limites das regiões e aumentar seu máximo tamanho.
Como os dados de uma região podem ser armazenados em vários HFiles, o Hbase os mescla periodicamente para acelerar o trabalho. Esta operação é chamada de compactação no Hbase. As compactações são de dois tipos:
- Compactação menor . Inicia automaticamente, é executado em segundo plano. Tem uma prioridade baixa em comparação com outras operações do Hbase.
- Grande compactação . É iniciado manualmente ou mediante a ocorrência de determinados gatilhos (por exemplo, por um temporizador). Ele tem alta prioridade e pode reduzir significativamente a velocidade do cluster. As compactações principais são melhor executadas em um momento em que a carga no cluster é pequena. A compactação principal também exclui fisicamente os dados previamente marcados com lápide.
6.5 Formas de trabalhar com HBase
HBase Shell
A maneira mais fácil de começar a usar o Hbase é usar o utilitário shell hbase. Ele está disponível imediatamente após a instalação do hbase em qualquer nó do cluster hbase.![](https://cdn.codegym.cc/images/article/d13e20e1-fed9-444a-929c-15ab43d8209b/800.jpeg)
Hbase shell é um console jruby com suporte integrado para todas as operações básicas do Hbase. Veja a seguir um exemplo de criação de uma tabela de usuários com duas famílias de colunas, fazendo algumas manipulações nela e descartando a tabela no final no shell hbase:
create 'users', {NAME => 'user_profile', VERSIONS => 5}, {NAME => 'user_posts', VERSIONS => 1231231231}
put 'users', 'id1', 'user_profile:name', 'alexander'
put 'users', 'id1', 'user_profile:second_name', 'alexander'
get 'users', 'id1'
put 'users', 'id1', 'user_profile:second_name', 'kuznetsov'
get 'users', 'id1'
get 'users', 'id1', {COLUMN => 'user_profile:second_name', VERSIONS => 5}
put 'users', 'id2', 'user_profile:name', 'vasiliy'
put 'users', 'id2', 'user_profile:second_name', 'ivanov'
scan 'users', {COLUMN => 'user_profile:second_name', VERSIONS => 5}
delete 'users', 'id1', 'user_profile:second_name'
get 'users', 'id1'
disable 'users'
drop 'users'
API nativa
Como a maioria dos outros projetos relacionados a hadoop, o hbase é implementado em java, portanto, a API nativa está disponível em Java. A API nativa está bem documentada no site oficial. Aqui está um exemplo de uso da API Hbase retirada de lá:
import java.io.IOException;
import org.apache.hadoop.hbase.*;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.util.Bytes;
public class MyLittleHBaseClient {
public static void main(String[] args) throws IOException {
Configuration config = HBaseConfiguration.create();
Connection connection = ConnectionFactory.createConnection(config);
try {
Table table = connection.getTable(TableName.valueOf("myLittleHBaseTable"));
try {
Put p = new Put(Bytes.toBytes("myLittleRow"));
p.add(Bytes.toBytes("myLittleFamily"), Bytes.toBytes("someQualifier"),
Bytes.toBytes("Some Value"));
table.put(p);
Get g = new Get(Bytes.toBytes("myLittleRow"));
Result r = table.get(g);
byte [] value = r.getValue(Bytes.toBytes("myLittleFamily"),
Bytes.toBytes("someQualifier"));
String valueStr = Bytes.toString(value);
System.out.println("GET: " + valueStr);
Scan s = new Scan();
s.addColumn(Bytes.toBytes("myLittleFamily"), Bytes.toBytes("someQualifier"));
ResultScanner scanner = table.getScanner(s);
try {
for (Result rr = scanner.next(); rr != null; rr = scanner.next()) {
System.out.println("Found row: " + rr);
}
} finally {
scanner.close();
}
} finally {
if (table != null) table.close();
}
} finally {
connection.close();
}
}
}
Thrift, REST e suporte para outras linguagens de programação
Para trabalhar a partir de outras linguagens de programação, o Hbase fornece Thrift API e Rest API. Com base neles, os clientes são construídos para todas as principais linguagens de programação: python, PHP, Java Script, etc.
6.6 Alguns recursos de trabalho com HBase
-
O Hbase integra-se imediatamente com o MapReduce e pode ser usado como entrada e saída usando o TableInputFormat e o TableOutputFormat especiais.
-
É muito importante escolher a RowKey correta. O RowKey deve fornecer uma boa distribuição uniforme entre as regiões, caso contrário, existe o risco das chamadas "regiões quentes" - regiões que são usadas com muito mais frequência do que outras, o que leva ao uso ineficiente dos recursos do sistema.
-
Se os dados não forem carregados individualmente, mas imediatamente em grandes lotes, o Hbase oferece suporte a um mecanismo BulkLoad especial que permite carregar dados muito mais rapidamente do que usando Puts únicos. BulkLoad é essencialmente uma operação de duas etapas:
- Formação de HFile sem a participação de puts usando um trabalho MapReduce especial
- Inserindo esses arquivos diretamente no Hbase
-
O Hbase oferece suporte à saída de suas métricas para o servidor de monitoramento Ganglia. Isso pode ser muito útil ao administrar o Hbase para chegar ao fundo dos problemas do hbase.
chave de linha
A RowKey é o ID do usuário, que é um GUUID, uma string especialmente gerada para ser única em todo o mundo. Os GUUIDs são distribuídos uniformemente, o que proporciona uma boa distribuição de dados entre os servidores.
Família de colunas
Nosso armazenamento usa duas famílias de colunas:
- dados. Esse grupo de colunas armazena dados que não são mais relevantes para fins publicitários, como o fato de um usuário ter visitado determinados URLs. O TTL para esta família de colunas é definido como 2 meses, o limite para o número de versões é 2.000.
- longdata. Esse grupo de colunas armazena dados que não perdem sua relevância com o tempo, como sexo, data de nascimento e outras características “eternas” do usuário.
caixas de som
Cada tipo de fatos do usuário é armazenado em uma coluna separada. Por exemplo, a coluna Data:_v armazena os URLs visitados pelo usuário e a coluna LongData:gender armazena o gênero do usuário.
O carimbo de data/hora desse fato é armazenado como um carimbo de data/hora. Por exemplo, na coluna Data:_v, o timestamp é a hora em que o usuário visitou um URL específico.
Essa estrutura de armazenamento de dados do usuário se encaixa muito bem com nosso padrão de uso e permite que você atualize rapidamente os dados do usuário, obtenha rapidamente todas as informações necessárias sobre os usuários e, usando o MapReduce, processe rapidamente os dados sobre todos os usuários de uma só vez.
6.7 Alternativas
O HBase é bastante complexo para administrar e usar, então antes de usar o HBase faz sentido olhar para as alternativas:
-
Bancos de Dados Relacionais . Uma alternativa muito boa, especialmente no caso em que os dados cabem em uma máquina. Além disso, antes de tudo, você deve pensar em bancos de dados relacionais no caso de transações de índices diferentes do primário serem importantes.
-
Armazenamento de valor-chave . Armazenamentos como Redis e Aerospike são mais adequados quando a latência é necessária e o processamento em lote é menos importante.
-
Arquivos e seu processamento com MapReduce . Se os dados forem apenas adicionados e raramente atualizados/alterados, é melhor não usar o HBase, mas simplesmente armazenar os dados em arquivos. Para simplificar o trabalho com arquivos, você pode usar ferramentas como Hive, Pig e Impala.
O uso do HBase é justificado quando:
- Há muitos dados e eles não cabem em um computador / servidor
- Os dados são frequentemente atualizados e excluídos
- Existe uma “chave” explícita nos dados, à qual é conveniente vincular todo o resto
- Precisa de processamento em lote
- Precisa de acesso aleatório aos dados por chaves específicas
GO TO FULL VERSION