Comment résoudre la limite de nombre de sous-répertoires Linux?


9

J'ai un site Web qui stockera des images de profil utilisateur. Chaque image est stockée dans un répertoire (Linux) spécifique à l'utilisateur. Actuellement, j'ai une base de clients de 30+, ce qui signifie que j'aurai plus de 30 dossiers. Mais ma boîte Linux actuelle (ext2 / ext3) ne prend pas en charge la création de plus de 32 000 répertoires. Comment puis-je surmonter cela? Même les gars de YouTube ont le même problème, avec les vignettes vidéo. Mais ils l'ont résolu en passant à ReiserFS. Ne pouvons-nous pas avoir une meilleure solution?

Mise à jour: Lorsqu'on leur a demandé dans IRC, les gens demandaient de le mettre à niveau vers ext4, qui a une limite de 64k et bien sûr, vous pouvez même dépasser cela aussi . Ou piratage du noyau pour changer la limite.

Mise à jour: que diriez-vous de diviser la base d'utilisateurs en dossiers en fonction de la plage d'ID utilisateur. Cela signifie 1-1000 dans un dossier, 1000-2000 dans l'autre comme ça. Cela semble simple. Que dites-vous, les gars?

Franchement, n'y a-t-il pas d'autre moyen?


1
Pourquoi ne voulez-vous pas changer le système de fichiers? S'il s'agit d'une limitation de ext2 / 3, vous n'aurez d'autre changement que de changer le système de fichiers ou de diviser le FS actuel en FS plus petits (plus de points de montage différents).
Manuel Faux

1
Manuel: S'il change le système de fichiers, il associe un FS spécifique à son application. Bien que cela puisse finir par être la réponse, je dirais que c'est probablement un problème qui doit être résolu au niveau de l'application. Si vous avez besoin de pirater le noyau ou le système de fichiers, vous empruntez probablement le mauvais chemin à moins d'exigences très particulières.
Kyle Brandt

Réponses:


16

Cette limite est par répertoire, pas pour l'ensemble du système de fichiers, vous pouvez donc le contourner en subdivisant davantage les choses. Par exemple, au lieu d'avoir tous les sous-répertoires utilisateur dans le même répertoire, divisez-les par les deux premiers caractères du nom, vous avez donc quelque chose comme:

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

Encore mieux serait de créer une forme de hachage des noms et de l'utiliser pour la division. De cette façon, vous obtiendrez une meilleure répartition parmi les répertoires au lieu de, avec l'exemple des lettres initiales, "da" étant très plein et "zz" complètement vide. Par exemple, si vous prenez le nom CRC ou MD5 et utilisez les 8 premiers bits, vous obtiendrez quelque chose comme:

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

Cela peut être étendu à d'autres profondeurs selon les besoins, par exemple comme si vous n'utilisez pas le nom d'utilisateur comme valeur de hachage:

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

Cette méthode est utilisée dans de nombreux endroits comme le cache de Squid, pour copier l'exemple de Ludwig et les caches locaux des navigateurs Web.

Une chose importante à noter est qu'avec ext2 / 3, vous commencerez à rencontrer des problèmes de performances avant de vous approcher de la limite de 32 000, car les répertoires sont recherchés de manière linéaire. Le passage à un autre système de fichiers (ext4 ou reiser par exemple) supprimera cette inefficacité (reiser recherche les répertoires avec un algorithme divisé en binaires, de sorte que les longs répertoires sont gérés beaucoup plus efficacement, ext4 peut également le faire) ainsi que la limite fixe par répertoire.


Je viens de mettre à jour la description de la question pour inclure ceci: "Mise à jour: que diriez-vous de diviser la base d'utilisateurs en dossiers en fonction de la plage d'ID utilisateur. Signifiant 1-1000 dans un dossier, 1000-2000 dans l'autre comme ça. Cela semble simple. dites-vous?"
None-da

1
Cela fonctionnerait bien et serait plus efficace qu'un hachage, si les utilisateurs sont généralement identifiés par l'ID utilisateur au lieu (ou aussi) du nom d'utilisateur. Cependant, si vous vous référez toujours à eux par leur nom ailleurs dans le système, vous devrez ajouter des recherches supplémentaires de nom-> id partout.
David Spillett

Merci David! J'ai essayé une solution encore différente. J'ai créé à peine 4 dossiers avec la plage 1-30000, 30000-60000 etc. Je pense que l'obtention d'un fichier à partir d'un si gros répertoire prendra plus de temps que d'un répertoire qui contient 1000 fichiers (approche précédente). Que dis-tu?
None-da

1
Cela dépend du système de fichiers. Si vous utilisez ext2 ou ext3, je recommanderais beaucoup moins que 30 000 par répertoire. Certains outils émettent des avertissements d'environ 10 000. Vous pouvez activer l'indexation des répertoires dans ext3 / 4 pour vous aider: tune2fs -O dir_index / dev / <volumename> mais en gardant simplement le nombre d'objets dans un répertoire plus bas (quelques milliers ou moins?) Est ce que je recommanderais ici .
David Spillett

@Maddy, vous voulez cette solution en raison d'autres limitations sur la façon dont Ext2 / 3 gère un grand nombre de fichiers. Voir serverfault.com/questions/43133/… pour plus de détails. Décomposer les noms en sous-répertoires en tant que sous-répertoires atténue les autres problèmes que vous auriez éventuellement rencontrés. Notez que c'est la même stratégie que Squid utilise lorsqu'il configure le cache d'objets pour la première fois - par exemple, 64 répertoires contenant chacun 64 répertoires, à titre d'exemple.
Avery Payne

7

Si vous êtes lié à ext2 / ext3, la seule possibilité que je vois est de partitionner vos données. Trouvez un critère qui divise vos données en morceaux gérables de taille similaire.

Si ce n'est que sur les images de profil que je ferais:

  1. Utilisez un hachage (par exemple SHA1) de l'image
  2. Utiliser le SHA1 comme nom de fichier et de répertoire

Par exemple, le cache SQUID procède comme suit:

f / 4b / 353ac7303854033

Le répertoire de niveau supérieur est le premier chiffre hexadécimal, le deuxième niveau est les deux chiffres hexadécimaux suivants et le nom de fichier est le chiffre hexadécimal restant.


2

Ne pouvons-nous pas avoir une meilleure solution?

Vous avez une meilleure solution - utilisez un système de fichiers différent, il y en a beaucoup disponibles, dont beaucoup sont optimisés pour différentes tâches. Comme vous l'avez souligné, ReiserFS est optimisé pour gérer de nombreux fichiers dans un répertoire.

Voir ici pour une comparaison des systèmes de fichiers.

Soyez juste heureux que vous ne soyez pas bloqué avec NTFS qui est vraiment épouvantable pour beaucoup de fichiers dans un répertoire. Je recommanderais JFS en remplacement si vous ne souhaitez pas utiliser le FS4 ext4 relativement nouveau (mais apparemment stable).


Avez-vous de bons liens vers les performances du système de fichiers NTFS?
Thorbjørn Ravn Andersen

oui, à part l'expérience personnelle avec une application qui a été laissée trop longtemps pour créer de nouveaux fichiers dans un répertoire .. (il a fallu des heures pour les supprimer tous), et l'augmentation des performances de subversion en limitant le nombre de fichiers dans un répertoire à 1000. Ou lire : support.microsoft.com/kb/130694 Je ne pense pas qu'ils aient jamais "corrigé" cela comme il l'a toujours noté comme un perf. peaufiner pour NTFS.
gbjbaanb le

1

L'image de profil est-elle petite? Qu'en est-il de le mettre dans la base de données avec le reste des données de profil? Ce n'est peut-être pas la meilleure option pour vous, mais cela vaut la peine d'être considéré ...

Voici un livre blanc Microsoft (plus ancien) sur le sujet: Vers BLOB ou pas vers BLOB .


1

J'ai piraté ensemble une petite galerie Web, où je me suis retrouvé avec une variation de ce problème; Je n'avais "que" environ 30 000 images dans le répertoire de cache, ce qui s'est avéré assez lent (ext2 utilise des listes liées pour les index de répertoire, si je me souviens bien).

J'ai fini par faire quelque chose dans ce sens:

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

Cela partitionnera les données dans 256 répertoires, ce qui donnera une recherche de répertoire rapide pour chacun des trois niveaux.

  • J'ai choisi d'utiliser MD5 sur SHA-1, car MD5 garantit une sortie différente si vous modifiez 12 bits de 32, donc je trouve cela bien adapté au hachage des noms d'utilisateurs, des répertoires et d'autres éléments courts. Et c'est rapide aussi ...
  • Je n'inclus pas le hachage entier, car cela produira beaucoup trop de répertoires et supprimera efficacement le cache-disque encore et encore.

1
Vous pourriez probablement utiliser un hachage plus simple comme CRC, car le hachage n'a pas besoin d'être cryptographiquement solide comme MD5 ou SHA ... mais la différence de performances est probablement négligeable de toute façon ...
sleske

0

Ce n'est pas une réponse immédiate à votre problème, mais quelque chose à surveiller pour référence future est le projet lié à OpenBSD appelé 'Epitome'

Epitome est un moteur qui fournit des services de stockage à instance unique, de stockage adressable de contenu et de déduplication.

Toutes vos données sont stockées dans un magasin de données sous forme de blocs hachés, supprimant les blocs non uniques pour réduire l'utilisation de l'espace, et vous permet essentiellement d'oublier le mécanisme de stockage car vous pouvez simplement demander le contenu du magasin de données par UUID.

Epitome est actuellement expérimental, mais quelque chose à surveiller pour l'avenir.


0

Généralement, vous voulez éviter d'avoir des répertoires contenant un grand nombre de fichiers / répertoires. La raison principale est que l'expansion des caractères génériques sur la ligne de commande entraînera des erreurs "Trop d'arguments" entraînant beaucoup de douleur lors de la tentative de travail avec ces répertoires.

Optez pour une solution qui crée un arbre plus profond mais plus étroit, par exemple en créant des sous-dossiers comme d'autres l'ont décrit.


0

Nous avons eu un problème similaire, la solution - comme mentionné précédemment - est de créer une hiérarchie de répertoires.

Bien sûr, si vous avez une application complexe qui repose sur une structure de répertoire plate, vous aurez probablement besoin de beaucoup de correctifs. Il est donc bon de savoir qu'il existe une solution de contournement, utilisez des liens symboliques qui n'ont pas la limite de 32 Ko mentionnée. Ensuite, vous avez beaucoup de temps pour réparer l'application ...


0

Pourquoi ne pas utiliser une approche d'horodatage, puis avoir une option de débordement.

Par exemple

Disons donc que votre horodatage est: 1366587600

Omettez les 2 derniers chiffres (sinon cela devient un peu ridicule). Séparez le tampon en ensembles de 4 (le nombre de répertoires ne doit pas dépasser 9 999 - si vous le souhaitez, vous pouvez le séparer différemment).

Cela devrait vous laisser quelque chose comme ceci:

/files/1366/5876/

Ensuite, vérifiez également le montant dans le répertoire avant le téléchargement, s'il obtient un grand nombre de téléchargements (c'est-à-dire 32000 + par 100 secondes), puis parcourez le répertoire par la seconde ou une lettre, par exemple:

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

ou

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

Ensuite, connectez l'horodatage + la lettre ou le code de chemin complet dans une base de données avec l'utilisateur et vous devriez être défini.

pathstamp: 1366587600 ou 13665876a (si vous utilisez des lettres).

Cela se retrouve avec un grand nombre de répertoires, mais cela peut être très utile pour gérer les révisions de fichiers. Par exemple, si un utilisateur souhaite utiliser une nouvelle photo de profil, vous avez toujours l'ancienne version horodatée de l'ancienne au cas où il souhaiterait annuler les modifications (ce n'est pas seulement écrasé).


0

Je suggère de décider combien de sous-répertoires maximum vous voulez (ou pouvez) avoir dans le dossier parent.

Ensuite, vous devez convertir votre ID utilisateur pour qu'il commence à partir de 1.

Ensuite, vous pouvez faire: modulo = currentId % numberOfSubdirectories

modulocontiendra désormais votre numéro de sous-répertoire qui ne sera jamais supérieur à celui que numberOfSubdirectoriesvous avez choisi.

Faites ce que vous voulez avec modulo, hachez-le, par exemple.

De cette façon, les sous-répertoires seront également remplis de façon linéaire.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.