Partições e buckets do Hive com exemplo

⚡ Resumo Inteligente

Partições e buckets são as duas maneiras pelas quais o Apache Hive divide os dados da tabela no disco: uma partição cria um diretório para cada valor de chave, enquanto um bucket transforma as linhas em um número fixo de arquivos por meio de um hash.

  • 🗂️ Partição é um diretório: Cada valor distinto da chave de partição se torna seu próprio subdiretório dentro da pasta da tabela no HDFS.
  • ✂️ Poda de partições: Uma consulta que filtra pela chave de partição lê apenas os diretórios correspondentes, em vez de examinar a tabela inteira.
  • ⚙️ Modo dinâmico necessário: Carregar várias partições a partir de um SELECT requer que hive.exec.dynamic.partition.mode esteja definido como nonstrict.
  • 🧮 Bucket é um arquivo: A função CLUSTERED BY cria um hash da coluna escolhida e grava cada linha em um número fixo de arquivos.
  • 🔍 Amostragem e junções: As tabelas com buckets suportam leituras TABLESAMPLE eficientes e junções de buckets no lado do mapa, algo que as tabelas simples não conseguem fazer.
  • 📐 Escolha por cardinalidade: Particione os dados com base em colunas de baixa cardinalidade, como estado ou data, e agrupe colunas de alta cardinalidade, como identificadores de usuário.

Partições e buckets do Hive explicados com um exemplo prático.

Tabelas, partições e buckets são os componentes da modelagem de dados do Hive. Uma tabela define o esquema, uma partição divide essa tabela em diretórios no disco e um bucket divide os dados dentro de um diretório em um número fixo de arquivos.

O que são partições?

O Particionamento do Hive é uma forma de organizar tabelas em partições, dividindo-as em diferentes partes com base em chaves de partição. Fisicamente, cada partição é um subdiretório separado dentro da pasta da tabela no HDFS, o que permite ao Hive ignorar dados desnecessários.

O particionamento é útil quando a tabela possui uma ou mais chaves de partição. As chaves de partição são elementos básicos para determinar como os dados são armazenados na tabela. Uma chave de partição não é armazenada dentro dos próprios arquivos de dados – seu valor é codificado no nome do diretório, portanto, uma consulta filtrada por essa chave pode descartar diretórios inteiros antes que qualquer linha seja lida. Isso é chamado de eliminação de partições.

Por exemplo: -

“O cliente possui dados de comércio eletrônico referentes às operações na Índia, nos quais as operações de cada estado (38 estados) são mencionadas em conjunto. Se considerarmos a coluna de estado como chave de particionamento e realizarmos partições nesses dados da Índia como um todo, podemos obter um número de partições (38 partições) igual ao número de estados (38) presentes na Índia. Dessa forma, os dados de cada estado podem ser visualizados separadamente nas tabelas de partições.”

Amostra Code Trecho de código para partições

As seis instruções abaixo são executadas em sequência no shell do Hive. Cada uma delas representa uma etapa separada, e as capturas de tela a seguir mostram a mesma sequência sendo executada em um cluster em produção.

  1. Criação da tabela allstates
    create table allstates(state string, District string,Enrolments string)
    
    row format delimited
    
    fields terminated by ',';
    
  2. Carregando dados na tabela criada allstates
    Load data local inpath '/home/hduser/Desktop/AllStates.csv' into table allstates;
  3. Criação de tabela de partição
    create table state_part(District string,Enrolments string) PARTITIONED BY(state string);
  4. Para partição, temos que definir esta propriedade
    set hive.exec.dynamic.partition.mode=nonstrict
  5. Carregando dados na tabela de partição
    INSERT OVERWRITE TABLE state_part PARTITION(state)
    SELECT district,enrolments,state from  allstates;
  6. Processamento real e formação de tabelas de partição com base no estado como chave de partição

Haverá 38 partições de saída no armazenamento HDFS com o nome do arquivo como nome do estado. Verificaremos isso nesta etapa.

As capturas de tela a seguir mostram a execução do código mencionado acima.

Na primeira tela, a etapa 1 é executada no colmeia> prompt e cria o todos os estados Tabela com três colunas e uma vírgula como delimitador de campos.

Shell do Hive criando a tabela allstates com três colunas delimitadas.

A próxima tela abrange as etapas 2 e 3: o arquivo AllStates.csv é carregado em todos os estadose a tabela particionada parte_do_estado é criado com o estado declarado como a chave de partição.

Carregando o arquivo AllStates.csv na tabela allstates e criando a tabela particionada state_part.

Os passos 4 e 5 aparecem a seguir. O modo de particionamento dinâmico é definido como não estrito, e a instrução INSERT OVERWRITE inicia um trabalho MapReduce cujas linhas de log mostram uma partição sendo carregada para cada valor de estado.

A saída do trabalho MapReduce carrega uma partição do Hive por valor de estado.

A listagem do diretório do armazém no HDFS confirma o resultado da etapa 6 – o shell reporta 38 itens, um parte_do_estado/estado= lista telefônica por estado.

Listagem do HDFS mostrando 38 diretórios de partição state_part no warehouse do Hive.

A partir do código acima, fazemos o seguinte

  1. Criação da tabela allstates com 3 colunas, cujos nomes são: estado, distrito e matrículas.
  2. Carregando dados na tabela allstates
  3. Criação de tabela de partição com estado como chave de partição
  4. Nesta etapa, defina o modo de partição como não estrito (este modo ativará o modo de partição dinâmica).
  5. Carregando dados na parte de estado da tabela de partições
  6. Processamento real e formação de tabelas de partição com base no estado como chave de partição
  7. Haverá 38 partições de saída no armazenamento HDFS com o nome do arquivo como nome do estado. Nesta etapa, visualizamos as 38 partições de saída no HDFS.

Particionamento estático versus dinâmico no Hive

O exemplo acima utiliza particionamento dinâmico, mas o Hive suporta dois estilos de carregamento, e a diferença entre eles determina a quantidade de particionamento.ping – e o nível de risco – que cada carga acarreta.

Particionamento estático O valor da partição é especificado na própria instrução, portanto, o valor deve ser conhecido antes da execução do carregamento, e uma instrução preenche exatamente uma partição. Particionamento dinâmico Permite que o Hive leia o valor da partição da última coluna da lista SELECT e crie os diretórios em tempo de execução, razão pela qual o exemplo precisa apenas de um único INSERT para produzir 38 diretórios.

Aspecto Particionamento estático Particionamento dinâmico
Valor da partição Fornecido manualmente na cláusula de PARTIÇÃO Leia em tempo de execução da coluna SELECT.
Partições por instrução completa Muitos
Configuração Funciona no modo estrito padrão. É necessário definir hive.exec.dynamic.partition.mode como nonstrict.
Velocidade de carga Mais rápido, porque não é necessária nenhuma varredura de valor. Mais lento, porque o trabalho agrupa as linhas por chave.
Melhor adequado para Conjuntos pequenos e conhecidos, como uma carga diária. Conjuntos de chaves grandes ou desconhecidos, como 38 estados.

As cargas dinâmicas também são limitadas. O Hive limita quantas partições um job pode criar – por padrão, 100 por mapper ou reducer e 1000 para toda a instrução – e o job falha assim que qualquer um desses limites é ultrapassado. Portanto, uma chave de cardinalidade muito alta exige que esses limites sejam aumentados ou que se utilize um design diferente.

O que são baldes?

Os buckets no Hive são usados ​​para segregar os dados de uma tabela do Hive em vários arquivos ou diretórios. Eles são usados ​​para consultas eficientes e, diferentemente de uma partição, o número de buckets é fixo quando a tabela é criada, portanto, nunca cresce com o aumento dos dados.

  • Os dados presentes nessas partições podem ser divididos ainda mais em grupos.
  • A divisão é realizada com base no hash de colunas específicas que selecionamos na tabela.
  • Os buckets utilizam algum tipo de algoritmo de hash no backend para ler cada registro e colocá-lo nos buckets.
  • O balde em que uma linha cai é decidido por função_hash(coluna_de_bucketing) mod num_buckets, portanto, valores iguais sempre acabam no mesmo arquivo.
  • No Hive 0.x e 1.x, o agrupamento (bucketing) precisava ser habilitado com defina hive.enforce.bucketing=true; antes da inserção

Essa última configuração representa o histórico em um cluster atual: o manual do Apache Hive Observa-se que isso não é necessário a partir do Hive 2.x, porque o mecanismo agora seleciona automaticamente a contagem de redutores e a coluna de agrupamento da definição da tabela.

Passo 1) Criando Bucket conforme mostrado abaixo.

A tela abaixo mostra a instrução CREATE TABLE para balde de amostras, com a cláusula CLUSTERED BY na parte inferior fixando a contagem de buckets.

Instrução CREATE TABLE do Hive agrupando o bucket de amostra em quatro buckets.

Da captura de tela acima

  • Estamos criando um bucket de amostra com nomes de colunas como first_name, job_id, department, salary e country.
  • Estamos criando 4 baldes aqui.
  • Assim que os dados são carregados, eles são automaticamente colocados em 4 categorias.
  • A coluna "país" é a coluna de agrupamento, portanto, cada linha para um país é gravada no mesmo arquivo de agrupamento.

Passo 2) Carregando dados na tabela samplebucket

Partindo do pressuposto de que a tabela “employees” já foi criada no sistema Hive, nesta etapa veremos o carregamento dos dados da tabela employees para a tabela samplebucket.

Antes de começarmos a mover os dados dos funcionários para os grupos, certifique-se de que eles contenham colunas com nomes como nome, cargo, departamento, salário e país.

Aqui estamos carregando dados da tabela employees para o samplebucket – a tela abaixo mostra a instrução INSERT OVERWRITE que realiza a cópia.

A instrução INSERT OVERWRITE copia as linhas de funcionários para a tabela samplebucket.

Passo 3) Exibindo os 4 baldes criados na Etapa 1.

Listar o diretório de tabelas no HDFS mostra o resultado físico: quatro arquivos de dados numerados em vez de um.

Listagem HDFS dos quatro arquivos de bucket criados no diretório samplebucket.

Na captura de tela acima, podemos ver que os dados da tabela de funcionários foram transferidos para os 4 buckets criados na etapa 1.

Particionamento versus agrupamento (bucketing) no Hive: principais diferenças

Ambas as funcionalidades dividem uma tabela em partes menores, mas fazem isso em níveis diferentes do sistema de arquivos e resolvem problemas distintos. A tabela abaixo as compara lado a lado.

Ponto de comparação Particionamento Baldeamento
Unidade criada Um diretório por valor de chave Um arquivo por bucket de hash
Declarado com PARTICIONADO POR AGRUPADOS POR … EM n BALDES
Número de peças Cresce com o número de valores distintos. Corrigido na criação da tabela
Coluna armazenada em arquivos de dados Não – o valor reside no nome do diretório. Sim, a coluna permanece uma coluna normal.
Melhor tipo de coluna Baixa cardinalidade, como estado, ano ou país. Alta cardinalidade, como user_id ou transaction_id.
Benefício principal A poda de partições ignora diretórios desnecessários. Tamanhos de arquivo uniformes, amostragem barata e junções no mapa.

Os dois não são rivais. Um layout de produção comum particiona uma tabela de fatos por data e, em seguida, agrupa cada dia na chave de junção, de modo que uma consulta reduza a um diretório e, em seguida, faça a junção de um bucket para outro dentro dele.

Quando usar particionamento, agrupamento ou ambos?

A escolha entre elas começa com a cardinalidade da coluna e o formato das consultas que lerão a tabela.

  • divisória Quando as consultas quase sempre filtram pela mesma coluna de baixa cardinalidade e quando o número de valores distintos permanece na casa das centenas, em vez de milhões.
  • Balde Quando a coluna útil tiver muitos valores distintos para ser um diretório, ou quando a tabela for unida ou amostrada repetidamente nessa coluna.
  • Use ambos Para tabelas de fatos grandes: particione pela data e, em seguida, agrupe dentro de cada partição pela chave de junção.

O modo de falha a ser observado é o problema dos arquivos pequenos. O particionamento em uma coluna com cardinalidade muito alta — um carimbo de data/hora ou um identificador de cliente — produz milhares de diretórios minúsculos, cada um contendo um arquivo muito menor que o tamanho do bloco do HDFS. Isso infla a memória do NameNode e torna cada varredura mais lenta, que é exatamente o resultado que o particionamento deveria evitar. O agrupamento (bucketing) evita isso porque a contagem de arquivos é limitada pela definição da tabela.

O agrupamento (bucketing) tem sua própria ressalva: o layout só estará correto se cada gravador o respeitar. A contagem de buckets declarada na criação é um metadado; portanto, um trabalho que grava na tabela sem agrupá-la corretamente pode deixar arquivos que não correspondem ao layout declarado, e amostragens posteriores ou junções de buckets lerão as linhas erradas.

Perguntas Frequentes

Execute o comando SHOW PARTITIONS nome_da_tabela no shell do Hive. Ele lê o metastore e imprime uma linha por diretório de partição, o que é mais rápido do que listar o caminho do warehouse no HDFS e também confirma se o metastore está sincronizado.

O comando ALTER TABLE nome_da_tabela ADD PARTITION (state='Goa') registra um novo diretório, e o comando ALTER TABLE nome_da_tabela DROP PARTITION (state='Goa') o remove.ping Uma partição gerenciada exclui seus dados, enquanto a partição dropa.ping Uma solução externa apenas limpa a entrada do metastore.

Por padrão, o Hive limita as partições dinâmicas a 100 por nó e 1000 por instrução. Uma chave com mais valores distintos ultrapassa esse limite e interrompe a execução. Aumente os parâmetros `hive.exec.max.dynamic.partitions.pernode` e `hive.exec.max.dynamic.partitions`, ou escolha uma chave com menos valores distintos.

Não. Era necessário no Hive 0.x e 1.x para forçar a contagem de reducers a corresponder à contagem de buckets. O HIVE-12331 removeu isso no Hive 2.0, e o mecanismo agora deriva tanto a contagem de reducers quanto a coluna de clusterização da tabela.

TABLESAMPLE(BUCKET x OUT OF y ON column) lê apenas os arquivos correspondentes ao bucket, em vez de examinar tudo. Como as linhas foram indexadas com base na mesma coluna no momento da gravação, a amostra é repetível e muito mais eficiente do que um filtro de linha aleatório.

Dividir uma tabela em milhares de arquivos minúsculos desperdiça memória do NameNode e inicia uma tarefa por arquivo, o que torna as varreduras mais lentas. Geralmente, isso ocorre após uma chave de partição com cardinalidade muito alta. Chaves mais genéricas, agrupamento (bucketing) ou compactação de arquivos resolvem o problema.

Sim. A análise de logs de consultas e os modelos de aprendizado de máquina em ferramentas como o Cloudera Workload XM classificam as colunas por frequência de filtro, assimetria e cardinalidade, e então sugerem um layout. A sugestão ainda precisa ser revisada, pois não leva em consideração as cargas de trabalho planejadas.

O Copilot gera rapidamente instruções PARTITIONED BY e CLUSTERED BY a partir de um comentário, e assistentes de agentes podem gerar um script de carregamento completo. Sempre verifique a contagem de buckets e a chave geradas em relação à cardinalidade real, pois o modelo faz inferências apenas com base nos nomes.

Resuma esta postagem com: