11 votes

Façon efficace de stocker de nombreuses vignettes

Actuellement, je stocke toutes les miniatures dans un seul répertoire avec le nom de fichier étant le hachage md5 du chemin d'accès complet à l'image en taille réelle. Cependant, j'ai lu ici que cela pose des problèmes lorsque le répertoire atteint des milliers de fichiers. Ils seront localisés de plus en plus lentement par le système de fichiers Linux.

Quelles alternatives ai-je, étant donné que je ne peux localiser la miniature que par le chemin de l'image d'origine ? Les dates seraient les meilleures options, comme année/mois/jour/hachage_md5.jpg, mais cela nécessiterait que je stocke et lise la date de quelque part, ce qui ajouterait des étapes supplémentaires.

Je pensais diviser le md5, par exemple les deux premiers caractères = nom du sous-répertoire, le reste = nom de fichier. Cela me donnerait environ 15*15 sous-répertoires, mais j'aimerais entendre des options meilleures, merci !


Une autre idée que je viens d'avoir : créer un serveur séparé pour organiser les miniatures. Le serveur suivrait le nombre de miniatures et créerait des répertoires supplémentaires lorsque certaines limites sont atteintes et réutiliserait d'anciens répertoires lorsque des miniatures sont supprimées. L'inconvénient est que j'aurais besoin d'une base de données séparée qui associe les hachages aux chemins des miniatures :(

0 votes

Il n'est pas clair pour moi quel problème vous essayez de résoudre, ou même si c'est vraiment un problème. Vous voulez optimiser "l'efficacité", mais que voulez-vous dire ? Moins d'espace gaspillé sur le disque ? Temps de recherche le plus rapide? Avez-vous besoin de la correspondance inverse où vous avez le nom de la vignette mais souhaitez l'image en haute résolution, ou simplement du cas où vous avez l'image en haute résolution et voulez la vignette. Combien d'images avez-vous? Que se passe-t-il si vous renommez un répertoire d'images haute résolution ?

0 votes

Quelle est la taille des images haute résolution ? Quelle est la taille des miniatures ? Les images haute résolution sont-elles au format JPEG ? Avez-vous envisagé de stocker les miniatures à l'intérieur des images haute résolution ? Le temps de démarrage est-il important ? Votre application est-elle distribuée - vous pourriez charger les miniatures dans Redis peut-être.

7voto

Tigger Points 1904

Nous utilisons FreeBSD (système de fichiers UFS), pas Linux, donc certains détails peuvent être différents.

Contexte

Nous avons plusieurs millions de fichiers sur ce système qui doivent être servis le plus rapidement possible à partir d'un site Web, pour un accès individuel. Le système que nous utilisons a très bien fonctionné au cours des 16 dernières années.

Le serveur 1 (nommé : Tom) a le site Web utilisateur principal avec une configuration Apache assez standard et une base de données MySQL. Rien de spécial du tout.

Le serveur 2 (nommé : Jerry) est l'endroit où les fichiers utilisateur sont stockés et a été personnalisé pour une livraison rapide de ces petits fichiers.

Le disque dur de Jerry est optimisé lors de sa création pour s'assurer que nous ne manquons pas d'inodes - quelque chose à prendre en compte lorsque vous créez des millions de petits fichiers.

La configuration Apache de Jerry est ajustée pour des temps de connexion très courts et un accès à un seul fichier par connexion. Sans ces ajustements, vous aurez des connexions ouvertes qui gaspillent des ressources. Cette configuration Apache ne conviendrait pas du tout au système principal (Tom) et causerait plusieurs problèmes.

Comme vous servez des "miniatures", et non des demandes individuelles, vous pourriez avoir besoin d'une structure légèrement différente. Pour être honnête, je ne connais pas suffisamment vos besoins pour conseiller vraiment ce qui serait le mieux pour la configuration de votre serveur Web.

Historiquement, nous avons utilisé plusieurs disques SCSI à travers plusieurs serveurs. Pour le moment, nous avons un seul serveur avec des disques de 300 Mo/s. L'entreprise est en déclin depuis un certain temps (grâce à Facebook), mais nous traitons encore plus de 2 millions de demandes de fichiers par jour. À notre apogée, c'était plus de 10 millions par jour.

Notre structure (une réponse possible)

Tout sur Jerry est optimisé pour la livraison des petits fichiers et rien d'autre.

Jerry est un serveur Web, mais nous le traitons plus comme une base de données. Tout ce qui n'est pas nécessaire est supprimé.

Chaque fichier reçoit un identifiant de 4 caractères. L'ID est alphanumérique (0-9, a-z, A-Z). Cela vous donne 61*61*61*61 combinaisons (ou 13,845,841 ID).

Nous avons également plusieurs domaines, donc chaque domaine a un maximum de 13,845,841 ID. Nous étions très proches de cette limite sur les "domaines" populaires avant que Facebook n'arrive, et nous avions des plans prêts à être mis en œuvre qui permettraient des identifiants de 5 caractères, mais cela n'a pas été nécessaire à la fin.

Les recherches dans le système de fichiers sont très rapides si vous connaissez le chemin complet du fichier. C'est seulement lent si vous avez besoin de scanner pour des correspondances de fichiers. Nous en avons pleinement profité.

Chaque ID de 4 caractères est une série de répertoires. par exemple, aBc9 est /chemin/vers/a/B/c/9.

C'est un très grand nombre d'identifiants uniques à travers seulement 4 répertoires. Chaque répertoire a un maximum de 61 sous-répertoires. Créer des recherches rapides sans saturer l'index du système de fichiers.

Localisé dans le répertoire ./9 (le dernier répertoire dans l'ID) se trouvent les fichiers de métadonnées nécessaires et le fichier de données brut. Le nom du fichier de métadonnées est connu, tout comme le fichier de données. Nous avons également d'autres fichiers connus dans chaque dossier, mais vous avez compris l'idée.

Si un utilisateur met à jour ou vérifie les métadonnées, l'ID est connu, donc une demande des métadonnées est renvoyée.

Si le fichier de données est demandé, encore une fois, l'ID est connu, donc les données sont renvoyées. Aucun balayage ou vérification complexe n'est effectué.

Si l'ID est invalide, un résultat invalide est renvoyé.

Rien de complexe, tout pour la vitesse.

Nos problèmes

Lorsque vous parlez de millions de petits fichiers, il est possible de manquer d'inodes. Assurez-vous de prendre cela en compte lors de la création du disque pour le serveur dès le départ. Prévoyez à l'avance.

Nous avons désactivé et/ou modifié un certain nombre de vérifications système FreeBSD. Les tâches de maintenance cron ne sont pas conçues pour les systèmes avec autant de fichiers.

La configuration Apache a été un peu une affaire d'essais et d'erreurs pour la rendre parfaite. Mais une fois que vous y arrivez, le soulagement est énorme. Le mod_status d'Apache est très utile.

La toute première chose à faire est de désactiver tous les fichiers journaux d'Apache. Ensuite, désactivez tout et ne réactivez que ce dont vous avez besoin.

Le code pour la livraison (et l'enregistrement) des métadonnées et des données brutes est également très optimisé. Oubliez les bibliothèques de code. Chaque ligne de code a été vérifiée et revérifiée au fil des ans pour la vitesse.

Conclusion

Si vous avez vraiment beaucoup de miniatures, divisez le système. Servez les petits fichiers à partir d'un serveur dédié optimisé à cette fin. Gardez le système principal optimisé pour un usage plus standard.

Un système d'ID basé sur les répertoires (qu'ils soient de 4 caractères aléatoires ou des parties d'un MD5) peut être rapide tant que vous n'avez pas besoin de rechercher des fichiers.

Votre système d'exploitation de base devra être ajusté pour que les vérifications système ne consomment pas vos ressources système.

Désactivez la création de fichiers journaux du serveur Web. Vous n'aurez presque jamais besoin de cela et cela créera un goulot d'étranglement sur le système de fichiers. Si vous avez besoin de statistiques, vous pouvez obtenir un aperçu général à partir de mod_status.

Pour être très honnête, on ne sait pas vraiment assez d'informations sur votre cas individuel et vos besoins. Je doute de la pertinence de mon expérience personnelle.

Bonne chance !

1 votes

J'ai fait une chose très similaire en utilisant des GUID pour les noms de fichiers dans une arborescence de répertoires nommés à partir des premiers caractères. Cela n'était pas aussi efficace que le vôtre (j'aurais seulement 16 répertoires dans chaque répertoire). Une amélioration a été d'utiliser la date du fichier comme déterminant pour la profondeur dans l'arborescence où un fichier se trouverait. J'ai donc commencé avec 4 niveaux de répertoire, et lorsque cela a commencé à sembler trop chargé au 4ème niveau, j'ai créé un cinquième, avec de nouveaux fichiers allant à ce niveau. Se souvenir de la date de cette décision était tout ce dont j'avais besoin pour décider si je devais aller à 4 ou 5 répertoires de profondeur en cherchant un fichier. Et ainsi de suite.

5voto

jadook Points 328

La meilleure méthode, efficace, minimale et la plus simple est SeaweedFS

Depuis 2017, j'utilise SeaweedFS pour stocker environ 4 millions d'images jpeg chaque 24 heures. Actuellement, la base de données contient plus de 2 milliards d'enregistrements. Je n'ai jamais eu le moindre problème avec cela et cela économise beaucoup d'espace disque par rapport au stockage en tant que fichiers système.

Voici une brève présentation de l'auteur :

SeaweedFS est un système de fichiers distribué simple et hautement évolutif. Il y a deux objectifs :

  1. stocker des milliards de fichiers !
  2. servir rapidement les fichiers !

Détails :

Mon projet contient 2 images pour chaque événement, une vignette et l'autre en taille réelle. Dans la première phase du projet, j'ai stocké les images en tant que fichiers avec une structure de répertoire année/mois/jour/[vignette|complet].jpg mais après quelques jours, j'ai dû parcourir les fichiers et c'était un cauchemar et la réponse du disque était lente. De plus, en cas de suppression d'un grand nombre de fichiers (plus d'un million), cela prendrait des heures. J'ai donc décidé de me renseigner sur la façon dont les gros noms comme Google, Facebook, Instagram et Twitter stockaient des milliards d'images, et j'ai trouvé quelques vidéos sur YouTube qui expliquaient des parties des architectures, puis je suis tombé sur SeaweedFS et j'ai décidé de l'essayer et j'ai jeté un coup d'œil rapide au code source "version 0.76 en production" et tout semblait bien "pas de code suspect".
La seule remarque était que le logo était récupéré via un CDN plutôt que localement.

La beauté de SeaweedFS réside dans sa simplicité et sa stabilité, et c'est un genre de pierre précieuse cachée (jusqu'à maintenant). Outre sa capacité à stocker des milliards de fichiers et à y accéder en un instant, il purifie automatiquement les fichiers en fonction du TTL, une fonctionnalité très utile car la plupart des clients disposent d'une quantité finie de stockage, donc ils ne peuvent pas conserver toutes les données indéfiniment. Et la deuxième chose que j'apprécie est son économie de stockage, par exemple :

Dans mon serveur, chaque fichier consommait des multiples de 8 Ko d'espace disque (en raison de la structure du système de fichiers), donc même si la plupart de mes vignettes avaient une taille de 1 ou 2 Ko, elles consommaient 8 Ko, donc lorsque vous additionnez tous ces octets gaspillés, vous finissez par gaspiller une grande partie du stockage, dans SeaWeedFS, chaque métadonnée de fichier n'ajoute que 40 octets supplémentaires, et c'est un héritage !.

J'espère que cela vous aidera.

1voto

Faraaz Malak Points 309

Si vous utilisez les 2 premiers caractères du md5 comme nom de dossier, et supposez que vous ayez 100 miniatures, avec seulement 2 miniatures partageant les 2 premiers caractères du nom de fichier en commun, vous rencontreriez bientôt le problème d'un système de fichiers lent.

Pouvez-vous s'il vous plaît partager la structure du répertoire où les images originales sont stockées?

Peut-être pouvez-vous créer la structure de répertoire pour les miniatures, basée sur la date de création de l'image originale?

Supposez que l'image originale a été créée le 3 mai 2019, alors la structure du répertoire des miniatures pourrait être miniatures/52019/abc123.jpg. (Considérez abc123 comme un hash)

Donc, pour localiser la miniature ci-dessus, vous devez :

  1. Lire la date de création de l'image originale
  2. Calculer le hash md5 du chemin complet de l'image originale (Dans ce cas, c'est abc123)
  3. Aller dans le dossier miniatures
  4. Localiser le sous-dossier, basé sur la date de création de l'image originale. Dans ce cas, c'est 52019
  5. Rechercher le fichier en utilisant le hash du chemin complet de l'image originale

J'espère que cela répond bien à votre question.

1voto

x00 Points 11008

J'ai lu ici que cela pose des problèmes lorsque le répertoire atteint des milliers de fichiers

  1. Ça me semble être une optimisation prématurée. Tu t'inquiètes pour des milliers. Mais pour l'instant, j'ai environ 10 000 fichiers dans le répertoire ~/.cache/thumbnails et je n'ai aucun problème avec ça. De combien de miniatures as-tu vraiment besoin? Fais-les! Et ensuite teste tes performances.

  2. Où l'avez-vous lu? Quels étaient les problèmes exactement décrits là-bas? Parce que à partir de ceci et cela vous pouvez voir que même avec un demi-million de fichiers dans un seul répertoire, vous pouvez y accéder assez rapidement. Oui, vous aurez du mal avec des répertoires énormes lorsque vous utiliserez certains outils (comme ls), mais vous pouvez sûrement écrire un meilleur serveur.

  3. Et, en option, vous pouvez créer une structure de répertoire parallèle. Ainsi, pour un fichier z/y/x/image.png la miniature ira dans thumbnails/z/y/x/image.png. De cette façon, vous aurez les avantages de:

    1. lisibilité humaine
    2. diff facile des arbres de répertoire des images originales et des miniatures en cas de bugs
    3. pas besoin de hachages md5
    4. code plus simple au cas où vous auriez besoin de certaines opérations par lot (comme supprimer toutes les miniatures des fichiers de z/y/x/)

    Cela peut aussi être plus efficace. Mais je ne suis pas sûr - testez-le.

0 votes

Ok peut-être est-ce une éjaculation précoce, mais pourquoi est-ce une mauvaise chose ? Il vaut mieux optimiser le logiciel maintenant, que plus tard quand il devient beaucoup plus complexe

0 votes

@Alex, oui, mais non :) Parfois c'est vrai, mais cette décision particulière sera sûrement locale à une seule fonction. Donc si vous décidez de la changer, cela nécessitera une modification de cette seule fonction. Je suppose que cela prendra moins de temps que vous n'en passerez à deviner la meilleure option. Donc la complexité de l'application dans son ensemble ne devrait pas importe.

1voto

Luctia Points 13

Je ne suis pas sûr du type d'application que vous êtes en train de construire, mais en fonction du nombre d'utilisateurs, de la vitesse de votre serveur et de la fréquence à laquelle les miniatures sont consultées, vous pourriez peut-être utiliser un système de type cache ? Stockez les miniatures générées comme vous le proposez, avec des hash MD5, et supprimez-les après un certain laps de temps. Si les miniatures sont consultées principalement lorsque les images sont d'abord mises sur le serveur et que leur utilisation diminue au fil du temps, vous pouvez simplement les supprimer (au milieu de la nuit, ou chaque fois qu'elles sont le moins utilisées) et les régénérer si elles sont à nouveau nécessaires, à condition que cela ne soit pas fait souvent.

Une autre option que vous pourriez avoir en fonction de la structure du répertoire de vos fichiers originaux est de séparer vos fichiers originaux en répertoires et de stocker les miniatures dans un répertoire du répertoire de leur origine. De cette manière, si vous connaissez le chemin de l'original, vous connaissez déjà une grande partie du chemin de la miniature.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X