Milhões de (pequenos) arquivos de texto em uma pasta


15

Gostaríamos de armazenar milhões de arquivos de texto em um sistema de arquivos Linux, com o objetivo de poder compactar e servir uma coleção arbitrária como um serviço. Tentamos outras soluções, como um banco de dados de chave / valor, mas nossos requisitos de simultaneidade e paralelismo tornam a melhor escolha usar o sistema de arquivos nativo.

A maneira mais direta é armazenar todos os arquivos em uma pasta:

$ ls text_files/
1.txt
2.txt
3.txt

o que deve ser possível em um sistema de arquivos EXT4 , que não tem limite para o número de arquivos em uma pasta.

Os dois processos de FS serão:

  1. Escreva um arquivo de texto a partir do scrape da web (não deve ser afetado pelo número de arquivos na pasta).
  2. Zip arquivos selecionados, fornecidos pela lista de nomes de arquivos.

Minha pergunta é: o armazenamento de até dez milhões de arquivos em uma pasta afetará o desempenho das operações acima, ou o desempenho geral do sistema, de maneira diferente da criação de uma árvore de subpastas para os arquivos residirem?


4
Relacionado: Como corrigir erros intermitentes de "Não resta espaço no dispositivo" durante o MV, quando o dispositivo tem bastante espaço . O uso dir_index, que geralmente é ativado por padrão, agiliza as pesquisas, mas pode limitar o número de arquivos por diretório.
Mark Plotnick

Por que não experimentá-lo rapidamente em uma máquina virtual e ver como é? Com o bash, é trivial preencher uma pasta com um milhão de arquivos de texto com caracteres aleatórios. Sinto que você obterá informações realmente úteis dessa maneira, além do que aprenderá aqui.
21717 JoshuaD

2
@ JosuéD: Se você preencher tudo de uma vez em um FS novo, é provável que todos os inodes sejam contíguos no disco, ls -lou qualquer outra coisa que seja statcada inode no diretório (por exemplo, bashglobbing / tab tab) será artificialmente mais rápido do que depois de algum desgaste (apague alguns arquivos, escreva alguns novos). O ext4 pode se sair melhor com isso do que o XFS, porque o XFS aloca espaço dinamicamente para inodes x dados, para que você possa acabar com inodes mais dispersos, eu acho. (Mas esse é um palpite puro baseado em muito pouco conhecimento detalhado; eu mal usei o ext4). Vá com abc/def/subdiretórios.
Peter Cordes

Sim, acho que o teste que sugeri poderá dizer ao OP "isso funcionará", mas definitivamente poderia rapidamente dizer a ele "isso não funcionará", o que é útil.
21417 JoshuaD

1
mas nossos requisitos de simultaneidade e paralelismo tornam o uso do sistema de arquivos nativo a melhor opção O que você tentou? De antemão, acho que mesmo um RDBMS de ponta, como o MySQL e um servlet Java que cria arquivos zipZipOutputStream rapidamente, superaria praticamente qualquer sistema de arquivos nativo gratuito do Linux - duvido que você queira pagar pelo GPFS da IBM. O loop para processar um conjunto de resultados JDBC e criar esse fluxo zip é provavelmente apenas de 6 a 8 linhas de código Java.
Andrew Henle

Respostas:


10

O lscomando, ou mesmo a conclusão do TAB ou a expansão do curinga pelo shell, normalmente apresentam seus resultados em ordem alfanumérica. Isso requer a leitura de toda a lista de diretórios e a classificação. Com dez milhões de arquivos em um único diretório, essa operação de classificação levará um tempo não negligenciável.

Se você puder resistir ao desejo de concluir a TAB e, por exemplo, escrever os nomes dos arquivos a serem compactados na íntegra, não deverá haver problemas.

Outro problema com os curingas pode ser a expansão de curingas, possivelmente produzindo mais nomes de arquivos do que cabem em uma linha de comando de tamanho máximo. O tamanho máximo típico da linha de comando será mais que adequado para a maioria das situações, mas quando falamos de milhões de arquivos em um único diretório, isso não é mais uma suposição segura. Quando um comprimento máximo da linha de comando é excedido na expansão de curinga, a maioria dos shells simplesmente falha na linha de comando inteira sem executá-la.

Isso pode ser resolvido executando suas operações curinga usando o findcomando:

find <directory> -name '<wildcard expression>' -exec <command> {} \+

ou uma sintaxe semelhante sempre que possível. Ele find ... -exec ... \+levará em conta automaticamente o comprimento máximo da linha de comando e executará o comando quantas vezes forem necessárias, ajustando a quantidade máxima de nomes de arquivos para cada linha de comando.


Os sistemas de arquivos modernos usam árvores B, B + ou similares para manter as entradas do diretório. pt.wikipedia.org/wiki/HTree
dimm

4
Sim ... mas se o shell ou o lscomando não souberem que a lista de diretórios já está classificada, eles levarão algum tempo para executar o algoritmo de classificação de qualquer maneira. Além disso, o espaço do usuário pode estar usando uma ordem de classificação localizada (LC_COLLATE) que pode ser diferente do que o sistema de arquivos pode fazer internamente.
TelcoM

17

Isso é perigosamente próximo a uma pergunta / resposta baseada em opinião, mas tentarei fornecer alguns fatos com minhas opiniões.

  1. Se você tiver um número muito grande de arquivos em uma pasta, qualquer operação baseada em shell que tente enumerá-los (por exemplo mv * /somewhere/else) poderá falhar em expandir o curinga com êxito ou o resultado poderá ser muito grande para ser usado.
  2. ls levará mais tempo para enumerar um número muito grande de arquivos do que um pequeno número de arquivos.
  3. O sistema de arquivos poderá lidar com milhões de arquivos em um único diretório, mas as pessoas provavelmente terão dificuldades.

Uma recomendação é dividir o nome do arquivo em dois, três ou quatro caracteres e usá-los como subdiretórios. Por exemplo, somefilename.txtpode ser armazenado como som/efi/somefilename.txt. Se você estiver usando nomes numéricos, divida da direita para a esquerda em vez de da esquerda para a direita, para que haja uma distribuição mais uniforme. Por exemplo, 12345.txtpode ser armazenado como 345/12/12345.txt.

Você pode usar o equivalente a zip -j zipfile.zip path1/file1 path2/file2 ...para evitar incluir os caminhos do subdiretório intermediário no arquivo ZIP.

Se você estiver entregando esses arquivos de um servidor da Web (não tenho certeza se isso é relevante), é trivial ocultar essa estrutura em favor de um diretório virtual com regras de reescrita no Apache2. Eu diria que o mesmo vale para o Nginx.


A *expansão será bem-sucedida, a menos que você fique sem memória, mas, a menos que você aumente o limite do tamanho da pilha (no Linux) ou use um shell mvembutido ou embutido (ksh93, zsh), a execve()chamada do sistema poderá falhar com um erro E2BIG.
Stéphane Chazelas

@ StéphaneChazelas sim ok, minha escolha de palavras pode ter sido melhor, mas o efeito líquido para o usuário é praticamente o mesmo. Vou ver se consigo alterar as palavras um pouco sem ficar atolada em complexidade.
roaima 15/12

Apenas curioso, como você descompactaria esse arquivo zip se evitar incluir os caminhos de subdiretórios intermediários nele, sem se deparar com os problemas discutidos?
Octopus

1
@ Octopus, o OP afirma que o arquivo zip conterá " arquivos selecionados, fornecidos pela lista de nomes de arquivos ".
roaima

Eu recomendo usar zip -j - ...e canalizar o fluxo de saída diretamente para a conexão de rede do cliente zip -j zipfile.zip .... Gravar um arquivo zip no disco significa que o caminho dos dados é lido em disco-> compactar-> gravar em disco-> ler em disco-> enviar para o cliente. Isso pode triplicar os requisitos de E / S do disco sobre a leitura de disco-> compactar-> enviar para o cliente.
Andrew Henle

5

Eu administro um site que lida com um banco de dados para filmes, TV e videogames. Para cada uma delas, há várias imagens na TV contendo dezenas de imagens por programa (por exemplo, instantâneos de episódios etc.).

Acaba havendo muitos arquivos de imagem. Em algum lugar na faixa de mais de 250.000. Tudo isso é armazenado em um dispositivo de armazenamento em bloco montado, onde o tempo de acesso é razoável.

Minha primeira tentativa de armazenar as imagens foi em uma única pasta, como /mnt/images/UUID.jpg

Encontrei os seguintes desafios.

  • lsatravés de um terminal remoto seria apenas travar. O processo seria zumbi e CTRL+Cnão o quebraria.
  • antes de chegar a esse ponto, qualquer lscomando preencheria rapidamente o buffer de saída e CTRL+Cnão pararia a rolagem sem fim.
  • Compactar 250.000 arquivos de uma única pasta levou cerca de 2 horas. Você deve executar o comando zip desconectado do terminal, caso contrário, qualquer interrupção na conexão significa que você deve iniciar novamente.
  • Não me arriscaria a tentar usar o arquivo zip no Windows.
  • A pasta rapidamente se tornou uma zona proibida para humanos .

Acabei tendo que armazenar os arquivos em subpastas usando o tempo de criação para criar o caminho. Tais como /mnt/images/YYYY/MM/DD/UUID.jpg. Isso resolveu todos os problemas acima e me permitiu criar arquivos zip direcionados a uma data.

Se o único identificador para um arquivo que você possui é um número numérico, e esses números tendem a ser executados em sequência. Por que não agrupá-los por 100000, 10000e 1000.

Por exemplo, se você tiver um arquivo chamado, 384295.txto caminho seria:

/mnt/file/300000/80000/4000/295.txt

Se você souber, alcançará alguns milhões. Use 0prefixos para 1.000.000

/mnt/file/000000/300000/80000/4000/295.txt

1

Escreva um arquivo de texto a partir do scrape da web (não deve ser afetado pelo número de arquivos na pasta).

Para criar um novo arquivo, é necessário varrer o arquivo do diretório procurando espaço vazio suficiente para a nova entrada do diretório. Se não houver um espaço grande o suficiente para armazenar a nova entrada de diretório, ela será colocada no final do arquivo de diretório. À medida que o número de arquivos em um diretório aumenta, o tempo para varrer o diretório também aumenta.

Enquanto os arquivos de diretório permanecerem no cache do sistema, o desempenho atingido não será ruim, mas se os dados forem liberados, a leitura do arquivo de diretório (geralmente altamente fragmentado) do disco poderá consumir um pouco de tempo. Um SSD melhora isso, mas para um diretório com milhões de arquivos, ainda pode haver um impacto perceptível no desempenho.

Zip arquivos selecionados, fornecidos pela lista de nomes de arquivos.

Também é provável que exija tempo adicional em um diretório com milhões de arquivos. Em um sistema de arquivos com entradas de diretório com hash (como EXT4), essa diferença é mínima.

armazenar até dez milhões de arquivos em uma pasta afetará o desempenho das operações acima, ou o desempenho geral do sistema, de maneira diferente da criação de uma árvore de subpastas para os arquivos residirem?

Uma árvore de subpastas não possui nenhuma das desvantagens de desempenho acima. Além disso, se o sistema de arquivos subjacente for alterado para não ter nomes de arquivos com hash, a metodologia da árvore ainda funcionará bem.


1

Primeiramente: impeça 'ls' de classificar com 'ls -U', talvez atualize seu ~ / bashrc para ter 'alias ls = "ls -U"' ou similar.

Para seu conjunto de arquivos grande, você pode tentar isso da seguinte maneira:

  • crie um conjunto de arquivos de teste

  • veja se muitos nomes de arquivos causam problemas

  • use o comportamento xargs parmeter-batching e zip (default) de adicionar arquivos a um zip para evitar problemas.

Isso funcionou bem:

# create ~ 100k files
seq 1 99999 | sed "s/\(.*\)/a_somewhat_long_filename_as_a_prefix_to_exercise_zip_parameter_processing_\1.txt/" | xargs touch
# see if zip can handle such a list of names
zip -q /tmp/bar.zip ./*
    bash: /usr/bin/zip: Argument list too long
# use xargs to batch sets of filenames to zip
find . -type f | xargs zip -q /tmp/foo.zip
l /tmp/foo.zip
    28692 -rw-r--r-- 1 jmullee jmullee 29377592 2017-12-16 20:12 /tmp/foo.zip
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.