Arquivos pequenos são um grande problema no Hadoop — ou, pelo menos, são se o número de perguntas na lista de usuários sobre esse tópico for suficiente. Neste post, examinarei o problema e examinarei algumas soluções comuns.
Problemas com arquivos pequenos e HDFS
Um arquivo pequeno é aquele que é significativamente menor que o tamanho do bloco HDFS (padrão 64 MB). Se você estiver armazenando arquivos pequenos, provavelmente terá muitos deles (caso contrário, não recorreria ao Hadoop), e o problema é que o HDFS não pode lidar com muitos arquivos.
Cada arquivo, diretório e bloco no HDFS é representado como um objeto na memória do namenode, cada um ocupando 150 bytes, como regra geral. Assim, 10 milhões de arquivos, cada um usando um bloco, usariam cerca de 3 gigabytes de memória. Ampliar muito além desse nível é um problema com o hardware atual. Certamente um bilhão de arquivos não é viável.
Além disso, o HDFS não está preparado para acessar arquivos pequenos com eficiência:ele foi projetado principalmente para acesso de streaming de arquivos grandes. Ler arquivos pequenos normalmente causa muitas buscas e muitos saltos de datanode para datanode para recuperar cada arquivo pequeno, o que é um padrão de acesso a dados ineficiente.
Problemas com arquivos pequenos e MapReduce
As tarefas de mapa geralmente processam um bloco de entrada de cada vez (usando o padrão
FileInputFormat
). Se o arquivo for muito pequeno e houver muitos deles, cada tarefa de mapa processará muito pouca entrada e haverá muito mais tarefas de mapa, cada uma das quais impõe uma sobrecarga extra de contabilidade. Compare um arquivo de 1 GB dividido em 16 blocos de 64 MB e 10.000 ou mais arquivos de 100 KB. Os 10.000 arquivos usam um mapa cada, e o tempo de trabalho pode ser dezenas ou centenas de vezes mais lento do que o equivalente com um único arquivo de entrada. Há alguns recursos para ajudar a aliviar a sobrecarga de contabilidade:reutilização de JVM de tarefa para executar várias tarefas de mapa em uma JVM, evitando assim alguma sobrecarga de inicialização da JVM (consulte o
mapred.job.reuse.jvm.num.tasks
código> propriedade) e MultiFileInputSplit
que pode executar mais de uma divisão por mapa. Por que os arquivos pequenos são produzidos?
Há pelo menos dois casos
- Os arquivos são partes de um arquivo lógico maior. Como o HDFS só tem suporte a anexos recentemente, um padrão muito comum para salvar arquivos ilimitados (por exemplo, arquivos de log) é gravá-los em partes no HDFS.
- Os arquivos são inerentemente pequenos. Imagine um grande corpus de imagens. Cada imagem é um arquivo distinto e não há uma maneira natural de combiná-las em um arquivo maior.
Esses dois casos requerem soluções diferentes. Para o primeiro caso, onde o arquivo é composto de registros, o problema pode ser evitado chamando o método
sync()
do HDFS método de vez em quando para gravar arquivos grandes continuamente. Alternativamente, é possível escrever um programa para concatenar os pequenos arquivos juntos. Para o segundo caso, é necessário algum tipo de container para agrupar os arquivos de alguma forma. O Hadoop oferece algumas opções aqui.
Arquivos HAR
Hadoop Archives (arquivos HAR) foram introduzidos no HDFS na versão 0.18.0 para aliviar o problema de muitos arquivos pressionando a memória do namenode. Os arquivos HAR funcionam construindo um sistema de arquivos em camadas sobre o HDFS. Um arquivo HAR é criado usando o
hadoop archive
comando, que executa um trabalho MapReduce para empacotar os arquivos que estão sendo arquivados em um pequeno número de arquivos HDFS. Para um cliente usando o sistema de arquivos HAR nada mudou:todos os arquivos originais são visíveis e acessíveis (embora usando um har:// URL). No entanto, o número de arquivos no HDFS foi reduzido. Ler arquivos em um HAR não é mais eficiente do que ler arquivos em HDFS e, de fato, pode ser mais lento, pois cada acesso a arquivos HAR requer duas leituras de arquivo de índice, bem como a leitura de arquivo de dados (consulte o diagrama). E embora os arquivos HAR possam ser usados como entrada para o MapReduce, não há mágica especial que permita que os mapas operem sobre todos os arquivos no co-residente HAR em um bloco HDFS. Deve ser possível construir um formato de entrada que possa aproveitar a localização aprimorada dos arquivos em HARs, mas ainda não existe. Observe que MultiFileInputSplit, mesmo com as melhorias no HADOOP-4565 para escolher arquivos em uma divisão que sejam locais do nó, precisará de uma busca por arquivo pequeno. Seria interessante ver o desempenho disso comparado a um SequenceFile, digamos. No momento atual, os HARs provavelmente são melhor usados puramente para fins de arquivamento.
Arquivos de sequência
A resposta usual para perguntas sobre “o problema dos arquivos pequenos” é:use um SequenceFile. A ideia aqui é que você use o nome do arquivo como a chave e o conteúdo do arquivo como o valor. Isso funciona muito bem na prática. Voltando aos 10.000 arquivos de 100 KB, você pode escrever um programa para colocá-los em um único SequenceFile e, em seguida, processá-los em fluxo contínuo (diretamente ou usando MapReduce) operando no SequenceFile. Há um par de bônus também. Os SequenceFiles são divisíveis, portanto o MapReduce pode dividi-los em partes e operar em cada parte independentemente. Eles também suportam compressão, ao contrário dos HARs. A compactação de blocos é a melhor opção na maioria dos casos, pois compacta blocos de vários registros (em vez de por registro).
Pode ser lento para converter dados existentes em SequenceFiles. No entanto, é perfeitamente possível criar uma coleção de SequenceFiles em paralelo. (Stuart Sierra escreveu um post muito útil sobre como converter um arquivo tar em um SequenceFile — ferramentas como essa são muito úteis, e seria bom ver mais delas). No futuro, é melhor projetar seu pipeline de dados para gravar os dados na origem diretamente em um SequenceFile, se possível, em vez de gravar em arquivos pequenos como uma etapa intermediária.
Ao contrário dos arquivos HAR, não há como listar todas as chaves em um SequenceFile, a não ser ler o arquivo inteiro. (MapFiles, que são como SequenceFiles com chaves classificadas, mantêm um índice parcial, então eles também não podem listar todas as suas chaves - veja o diagrama.)
SequenceFile é bastante centrado em Java. O TFile foi projetado para ser multiplataforma e substituir o SequenceFile, mas ainda não está disponível.
HBase
Se você estiver produzindo muitos arquivos pequenos, dependendo do padrão de acesso, um tipo diferente de armazenamento pode ser mais apropriado. O HBase armazena dados em MapFiles (SequenceFiles indexados) e é uma boa opção se você precisar fazer análises de streaming no estilo MapReduce com uma pesquisa aleatória ocasional. Se a latência for um problema, existem muitas outras opções – veja a excelente pesquisa de Richard Jones sobre lojas de valor-chave.