Como resolver o limite de número de subdiretórios linux?

9

Eu tenho um site que armazena imagens de perfil de usuário. Cada imagem é armazenada em um diretório (Linux) específico para o usuário. Atualmente, tenho uma base de clientes de mais de 30, o que significa que terei mais de 30 pastas. Mas minha caixa atual do Linux (ext2 / ext3) não suporta a criação de mais de 32000 diretórios. Como faço para superar isso? Até os caras do YouTube têm o mesmo problema, com miniaturas de vídeo. Mas eles resolveram o problema migrando para o ReiserFS. Não podemos ter uma solução melhor?

Atualização: Quando perguntadas no IRC, as pessoas estavam perguntando sobre a atualização para o ext4, que tem limite de 64k e, é claro, você também pode superar isso . Ou hackear o kernel para alterar o limite.

Atualização: que tal dividir a base de usuários em pastas com base no intervalo da identificação do usuário. Significado 1-1000 em uma pasta, 1000-2000 na outra assim. Isto parece ser simples. O que você diz, pessoal?

Francamente, não há outra maneira?

Nenhum da
fonte
1
Por que você não deseja alterar o sistema de arquivos? Se essa é uma limitação do ext2 / 3, você não terá outras alterações além de alterar o sistema de arquivos ou dividir o FS atual em FSs menores (pontos de montagem diferentes).
Manuel Faux
1
Manuel: Se ele altera o sistema de arquivos, está vinculando um FS específico ao seu aplicativo. Embora isso possa acabar sendo a resposta, provavelmente seria um problema que precisa ser resolvido no nível do aplicativo. Se você precisar hackear o kernel ou o sistema de arquivos, provavelmente estará seguindo o caminho errado, a menos que alguns requisitos muito especiais.
Kyle Brandt

Respostas:

16

Esse limite é por diretório, não para todo o sistema de arquivos; portanto, você pode contornar isso subdividindo ainda mais as coisas. Por exemplo, em vez de ter todos os subdiretórios de usuário no mesmo diretório, divida-os pelos dois primeiros caracteres do nome, para que você tenha algo como:

top_level_dir
|---aa
|   |---aardvark1
|   |---aardvark2
|---da
|   |---dan
|   |---david
|---do
    |---don

Melhor ainda seria criar algum tipo de hash dos nomes e usá-lo para a divisão. Dessa forma, você terá uma melhor distribuição entre os diretórios, em vez de, com o exemplo das letras iniciais, "da" estar muito cheio e "zz" completamente vazio. Por exemplo, se você pegar o nome do CRC ou MD5 e usar os primeiros 8 bits, obterá algo como:

top_level_dir
|---00
|   |---some_username
|   |---some_username
|---01
|   |---some_username
...
|---FF
|   |---some_username

Isso pode ser estendido para outras profundidades, conforme necessário, por exemplo, se o nome de usuário não for um valor de hash:

top_level_dir
|---a
|   |---a
|       |---aardvark1
|       |---aardvark2
|---d
    |---a
    |   |---dan
    |   |---david
    |---o
        |---don

Este método é usado em muitos lugares, como o cache do squid, para copiar o exemplo de Ludwig e os caches locais dos navegadores da web.

Uma coisa importante a ser observada é que, com o ext2 / 3, você começará a encontrar problemas de desempenho antes de chegar perto do limite de 32.000, pois os diretórios são pesquisados ​​linearmente. Mover para outro sistema de arquivos (ext4 ou reiser, por exemplo) removerá essa ineficiência (o reiser pesquisa diretórios com um algoritmo de divisão binária, para que diretórios longos sejam tratados com muito mais eficiência, o que o ext4 pode fazer também), bem como o limite fixo por diretório.

David Spillett
fonte
Apenas atualizei a descrição da pergunta para incluir isso: "Atualização: que tal dividir a base de usuários em pastas com base no intervalo da identificação do usuário. Significando 1-1000 em uma pasta, 1000-2000 na outra assim. Isso parece ser simples. O que você diz? "
Nenhum-da-
1
Isso funcionaria bem e seria mais eficiente que um hash, se os usuários forem geralmente identificados pelo ID do usuário em vez de (ou também) pelo nome de usuário. Embora se você sempre os referenciar por nome em outro lugar do sistema, precisará adicionar pesquisas adicionais de nome-> id em todo o lugar.
David Spillett
Obrigado David! Eu tentei solução ainda diferente. Eu criei quase 4 pastas com o intervalo 1-30000, 30000-60000 etc. Acho que obter um arquivo de um diretório tão grande levará mais tempo do que em um diretório com 1000 arquivos (abordagem anterior). O que você disse?
Nenhum-da-
1
Isso depende do sistema de arquivos. Se você estiver usando ext2 ou ext3, eu recomendaria muito menor que 30.000 por diretório. Algumas ferramentas emitem avisos sobre 10.000. Você pode ativar a indexação de diretório no ext3 / 4 para ajudar: tune2fs -O dir_index / dev / <nome do volume>, mas apenas mantendo o número de objetos em um diretório menor (alguns milhares ou menos?) É o que eu recomendaria aqui .
David Spillett
@ Maddy, você deseja esta solução devido a outras limitações sobre como o Ext2 / 3 lida com um grande número de arquivos. Consulte serverfault.com/questions/43133/… para obter mais detalhes. Dividir nomes em buckets como subdiretórios alivia outros problemas nos quais você eventualmente se depararia. Note que esta é a mesma estratégia que o Squid usa quando configura o cache de objetos pela primeira vez - por exemplo, 64 diretórios cada um com 64 diretórios dentro deles, apenas como exemplo.
Avery Payne
7

Se você está vinculado ao ext2 / ext3, a única possibilidade que vejo é particionar seus dados. Encontre um critério que divida seus dados em blocos gerenciáveis ​​de tamanho semelhante.

Se for apenas sobre as imagens de perfil que eu faria:

  1. Use um hash (por exemplo, SHA1) da imagem
  2. Use o SHA1 como nome de arquivo e diretório

Por exemplo, o cache SQUID faz desta maneira:

f / 4b / 353ac7303854033

O diretório de nível superior é o primeiro dígito hexadecimal, o segundo nível são os dois dígitos hexadecimais seguintes e o nome do arquivo é o dígito hexadecimal restante.

Ludwig Weinzierl
fonte
2

Não podemos ter uma solução melhor?

Você tem uma solução melhor - use um sistema de arquivos diferente, há muitos disponíveis, muitos dos quais são otimizados para tarefas diferentes. Como você apontou, o ReiserFS é otimizado para lidar com muitos arquivos em um diretório.

Veja aqui uma comparação dos sistemas de arquivos.

Apenas fique feliz por não estar preso ao NTFS, o que é realmente péssimo para muitos arquivos em um diretório. Eu recomendaria o JFS como um substituto se você não gosta de usar o relativamente novo (mas aparentemente estável) ext4 FS.

gbjbaanb
fonte
Você tem bons links para o desempenho do sistema de arquivos NTFS?
Thorbjørn Ravn Andersen
sim, além da experiência pessoal com um aplicativo que ficou muito tempo criando novos arquivos em um diretório .. (demorou horas para excluí-los todos), e o desempenho do subversion aumentou limitando o número de arquivos em um diretório para 1000. Ou leia : support.microsoft.com/kb/130694 Acho que eles nunca "consertaram" isso, como ainda era conhecido como um perf. ajustar para NTFS.
gbjbaanb 27/07/2009
1

A imagem do perfil é pequena? Que tal colocá-lo no banco de dados com o restante dos dados do perfil? Esta pode não ser a melhor opção para você, mas vale a pena considerar ...

Aqui está um white paper da Microsoft (mais antigo) sobre o tópico: BLOB ou não BLOB .

Kyle Brandt
fonte
1

Eu cortei uma pequena galeria na web, onde acabei com uma variação desse problema; Eu "apenas" tinha ~ 30.000 imagens no diretório de cache, o que acabou sendo bastante lento (ext2 usa listas vinculadas para índices de diretório, pelo que me lembro).

Acabei fazendo algo nesse sentido:

def key2path(key):
    hash = md5(key)
    return os.path.join(hash[0], hash[1], key)

Isso particionará os dados em 256 diretórios, o que fornece uma rápida pesquisa de diretório para cada um dos três níveis.

  • Eu escolhi usar o MD5 sobre o SHA-1, pois o MD5 garante uma saída diferente se você alterar 12 bits de 32, por isso acho um bom ajuste para hash de nomes de usuário, diretórios e outras coisas curtas. E é rápido também ...
  • Não incluo o hash inteiro, pois ele produzirá muitos diretórios e efetivamente trará o cache do disco repetidamente.
Morten Siebuhr
fonte
1
Você provavelmente poderia usar um hash mais simples como CRC, como o hash não precisa ser criptograficamente forte como MD5 ou SHA ... mas a diferença de desempenho é, provavelmente, de qualquer maneira desprezível ...
sleske
0

Não é uma resposta imediata para o seu problema, mas algo a se observar para referência futura é o projeto vinculado ao OpenBSD chamado 'Epitome'

Epitome é um mecanismo que fornece serviços de Armazenamento de instância única, Armazenamento endereçável de conteúdo e Desduplicação.

Todos os seus dados são armazenados em um armazenamento de dados como blocos de hash, removendo blocos não exclusivos para reduzir o uso de espaço e permitem que você esqueça basicamente o mecanismo de armazenamento, pois você pode simplesmente solicitar o conteúdo do armazenamento de dados pelo UUID.

O epítome é atualmente experimental, mas algo a ser observado no futuro.

Moo
fonte
0

Geralmente, você deseja evitar diretórios com um grande número de arquivos / diretórios. O principal motivo é que a expansão de curinga na linha de comando resultará em erros "Muitos argumentos", resultando em muita dor ao tentar trabalhar com esses diretórios.

Procure uma solução que crie uma árvore mais profunda, porém mais estreita, por exemplo, criando subpastas como as outras descritas.

Thorbjørn Ravn Andersen
fonte
0

Tivemos um problema semelhante, a solução - como mencionado anteriormente - é criar uma hierarquia de diretórios.

Obviamente, se você tem um aplicativo complexo que depende de uma estrutura de diretórios simples, provavelmente precisará de muitas correções. Portanto, é bom saber que existe uma solução alternativa, use links simbólicos que não tenham o limite de 32k mencionado. Então você tem bastante tempo para consertar o aplicativo ...

Karoly Horvath
fonte
0

Por que não usar uma abordagem de carimbo de data e hora e ter uma opção de estouro.

Por exemplo

Digamos que seu carimbo de data e hora seja: 1366587600

Omita os dois últimos dígitos (ou isso fica ridículo). Separe o carimbo em conjuntos de 4 (a contagem de diretórios não deve atingir mais de 9999 - se você quiser, pode separá-lo de maneira diferente).

Isso deve deixar você com algo assim:

/files/1366/5876/

Em seguida, verifique também a quantidade no diretório antes de fazer o upload, se estiver recebendo um grande número de uploads (por exemplo, 32000 + por 100 segundos), em seguida, itere o diretório pela segunda ou por uma letra, por exemplo:

/files/1366/5876/a/file.txt

ou

/files/1366/5876/00/file.txt

Em seguida, registre o carimbo de data / hora + letra ou o código do caminho completo em um banco de dados junto com o usuário e você deve estar definido.

pathstamp: 1366587600 ou 13665876a (se você estiver usando letras).

Isso acaba com um grande número de diretórios, mas pode ser realmente útil para lidar com revisões de arquivos. Por exemplo, se um usuário quiser usar uma nova imagem de perfil, você ainda terá a versão com carimbo de data e hora antiga da mais antiga, caso deseje desfazer as alterações (não é apenas sobrescrita).

Fireacer
fonte
0

Sugiro que você decida quantos subdiretórios máximos você deseja (ou pode) ter na pasta pai.

Então, você precisa converter seu ID de usuário para que eles comecem a partir de 1.

Então você pode fazer: modulo = currentId % numberOfSubdirectories

moduloagora conterá o número do subdiretório que nunca será maior do que numberOfSubdirectoriesvocê escolheu.

Faça o que quiser com o módulo, faça o hash, por exemplo.

Além disso, os subdiretórios dessa maneira serão preenchidos linearmente.

vitro
fonte