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

9

Eu tenho um site que armazenará 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 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 32.000 diretórios. Como faço para superar isso? Até mesmo os caras do YouTube têm o mesmo problema, com miniaturas de vídeo. Mas eles resolveram isso mudando para o ReiserFS. Não podemos ter uma solução melhor?

Atualização: Quando perguntado no IRC, as pessoas estavam perguntando sobre como atualizá-lo para o ext4, que tem limite de 64k e, claro, você pode até passar por isso também . Ou cortar o kernel para mudar o limite.

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

Francamente, não existe outro jeito?

    
por None-da 27.07.2009 / 10:55

10 respostas

16

Esse limite é por diretório, não para todo o sistema de arquivos, então você pode contorná-lo subdividindo ainda mais as coisas. Por exemplo, em vez de ter todos os subdiretórios do 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 alguma forma de hash dos nomes e usar isso para a divisão. Dessa forma, você obterá uma melhor distribuição entre os diretórios em vez de, com o exemplo das letras iniciais, "da" sendo muito completo e "zz" completamente vazio. Por exemplo, se você pegar o CRC ou MD5, o nome e usar os primeiros 8 bits, você terá 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, assim, se usar o nome de usuário, não um valor de hash:

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

Esse 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 notar é que com o ext2 / 3 você começará a atingir problemas de performance antes de chegar perto do limite de 32.000, pois os diretórios são procurados linearmente. Movendo para outro sistema de arquivos (ext4 ou reiser, por exemplo) irá remover essa ineficiência (reiser pesquisa diretórios com um algoritmo binário dividido, assim diretórios longos são manipulados com muito mais eficiência, ext4 também pode fazer) assim como o limite fixo por diretório.

    
por 27.07.2009 / 11:18
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 partes gerenciáveis de tamanho similar.

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 arquivo e nome do diretório

Por exemplo, o cache do SQUID faz assim:

f / 4b / 353ac7303854033

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

    
por 27.07.2009 / 11:04
2

Cant we have a better solution?

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

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

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

    
por 27.07.2009 / 11:18
1

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

Aqui está um whitepaper (mais antigo) da Microsoft sobre o tema: Para BLOB ou não para BLOB .

    
por 27.07.2009 / 13:36
1

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

Acabei fazendo algo ao longo destas linhas:

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 pesquisa rápida de diretórios para cada um dos três níveis.

  • Eu escolhi usar o MD5 em SHA-1, já que o MD5 garante uma saída diferente se você alterar 12 bits de 32, então acho que é um bom ajuste para hash de nomes de usuários, diretórios e outras coisas curtas. E é rápido também ...
  • Eu não incluo o hash inteiro, pois ele produzirá muitos diretórios e trash o cache de disco repetidas vezes.
por 27.07.2009 / 12:32
0

Não é uma resposta imediata para o seu problema, mas algo a ser observado para referência futura é o projeto vinculado do OpenBSD chamado 'Epítome'

O 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 repositório de dados como blocos hash, removendo blocos não exclusivos para reduzir o uso de espaço e permitindo que você essencialmente esqueça 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 que deve ser observado no futuro.

    
por 27.07.2009 / 13:55
0

Geralmente, você quer evitar ter diretórios com um grande número de arquivos / diretórios. A principal razão é que a expansão de curingas na linha de comando resultará em erros "Muitos argumentos", resultando em muita dificuldade ao tentar trabalhar com esses diretórios.

Procure uma solução que faça uma árvore mais profunda, mas mais estreita, por exemplo criando subpastas, como outros descreveram.

    
por 27.07.2009 / 19:00
0

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

É claro que se você tiver um aplicativo complexo que depende de uma estrutura de diretórios simples, provavelmente precisará de muitos patches. Portanto, é bom saber que existe uma solução alternativa, use links simbólicos que não possuem o limite de 32k mencionado. Então você tem bastante tempo para consertar o aplicativo ...

    
por 09.03.2011 / 03:02
0

Por que não usar uma abordagem de timestamp e, em seguida, ter uma opção de estouro.

Por exemplo

Então, vamos dizer que seu timestamp é: 1366587600

Omita os últimos 2 dígitos (ou então fica um pouco ridículo). Separe o carimbo em conjuntos de 4 (a contagem do diretório não deve chegar a mais de 9999 - se você quiser, pode separá-lo de forma diferente).

Isso deve deixar você com algo assim:

/files/1366/5876/

Em seguida, verifique também o valor dentro do diretório antes de fazer o upload, se ele estiver recebendo um grande número de uploads (por exemplo, 32000 + por 100 segundos) e iterar o diretório pelo segundo ou por uma letra, por exemplo:

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

ou

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

Em seguida, registre o registro de data e hora + letra ou o código de 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 muito útil para lidar com revisões de arquivos. Por exemplo, se um usuário quiser usar uma nova foto de perfil, você ainda terá a versão antiga com data e hora do antigo, caso deseje desfazer as alterações (não apenas sobrescreveu).

    
por 22.04.2013 / 02:11
0

Eu sugeriria decidir quantos subdiretórios máximos você quer (ou pode) ter na pasta pai.

Em seguida, você precisa converter seu ID de usuário para que ele comece em 1.

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

modulo agora conterá seu número de subdiretório que nunca será maior que numberOfSubdirectories que você escolheu.

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

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

    
por 08.11.2017 / 13:56