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

14

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 para simultaneidade e paralelismo tornam o uso do sistema de arquivos nativo a melhor opção.

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

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

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 FS serão:

  1. Grave um arquivo de texto do raspar da Web (não deve ser afetado pelo número de arquivos na pasta).
  2. Zipar arquivos selecionados, fornecidos pela lista de nomes de arquivos.

Minha pergunta é: armazenar até dez milhões de arquivos em uma pasta afetará o desempenho das operações acima ou o desempenho geral do sistema, diferentemente de criar uma árvore de subpastas para os arquivos?

    
por user1717828 15.12.2017 / 17:16

5 respostas

10

O comando ls , ou mesmo a conclusão de tabulação ou a expansão de curinga pelo shell, normalmente apresentará seus resultados em ordem alfanumérica. Isso requer ler toda a listagem de diretórios e classificá-la. Com dez milhões de arquivos em um único diretório, essa operação de classificação levará um tempo não desprezível.

Se você puder resistir ao desejo de completar a tecla TAB e, por exemplo, escrever os nomes dos arquivos a serem zipados na íntegra, não deve haver problemas.

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

Isso pode ser resolvido fazendo suas operações de caractere curinga usando o comando find :

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

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

    
por 15.12.2017 / 18:11
17

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

  1. Se você tiver um grande número de arquivos em uma pasta, qualquer operação baseada em shell que tentar enumerá-los (por exemplo, mv * /somewhere/else ) poderá falhar ao 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á manipular 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.txt pode ser armazenado como som/efi/somefilename.txt . Se você estiver usando nomes numéricos, divida da direita para a esquerda, em vez da esquerda para a direita, para que haja uma distribuição mais uniforme. Por exemplo, 12345.txt pode 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 disponibilizando esses arquivos a partir 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 reconfiguração no Apache2. Eu diria que o mesmo é verdade para o Nginx.

    
por 15.12.2017 / 18:03
5

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

Lá, acaba sendo um monte de arquivos de imagem. Em algum lugar na faixa de 250.000+. Estes são todos armazenados em um dispositivo de armazenamento de 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

Eu encontrei os seguintes desafios.

  • ls através de um terminal remoto apenas iria travar. O processo seria zumbi e CTRL+C não o quebraria.
  • antes de chegar a esse ponto, qualquer comando ls preencheria rapidamente o buffer de saída e CTRL+C não interromperia a rolagem infinita.
  • A compactação de 250.000 arquivos de uma única pasta demorou cerca de duas horas. Você deve executar o comando zip desconectado do terminal, caso contrário, qualquer interrupção na conexão significa que você deve começar tudo de novo.
  • Eu não arriscaria tentar usar o arquivo zip no Windows.
  • A pasta rapidamente se tornou uma zona sem humanos permitidos .

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

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

Por exemplo, se você tiver um arquivo chamado 384295.txt , o caminho será:

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

Se você sabe, chegará a alguns milhões. Use 0 prefixos para 1.000.000

/mnt/file/000000/300000/80000/4000/295.txt
    
por 16.12.2017 / 17:52
1

Write text file from web scrape (shouldn't be affected by number of files in folder).

Para criar um novo arquivo, é necessário verificar o arquivo do diretório procurando espaço vazio suficiente para a nova entrada de diretório. Se não houver espaço localizado 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.

Contanto que os arquivos de diretório permaneçam no cache do sistema, o impacto no desempenho disso não será ruim, mas se os dados forem liberados, a leitura do arquivo de diretório (geralmente altamente fragmentado) do disco pode consumir um pouco Tempo. Um SSD melhora isso, mas para um diretório com milhões de arquivos, ainda pode haver um impacto notável no desempenho.

Zip selected files, given by list of filenames.

Isso também requer tempo adicional em um diretório com milhões de arquivos. Em um sistema de arquivos com entradas de diretório hash (como EXT4), essa diferença é mínima.

will storing up to ten million files in a folder affect the performance of the above operations, or general system performance, any differently than making a tree of subfolders for the files to live in?

Uma árvore de subpastas não tem nenhum dos inconvenientes 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 de árvore ainda funcionará bem.

    
por 16.12.2017 / 09:55
1

Primeiramente: evite que o 'ls' troque com 'ls -U', talvez atualize seu ~ / bashrc para ter 'alias ls="ls -U"' ou similar.

Para o seu grande conjunto de arquivos, você pode tentar isso assim:

  • crie um conjunto de arquivos de teste

  • veja se muitos nomes de arquivos causam problemas

  • use o comportamento xargs parmeter-batching e zip (padrão) 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_.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
    
por 16.12.2017 / 21:20