Commencer
Un système de comptabilité et de reporting pris en charge par notre société a commencé à croître très rapidement dans la quantité de données stockées. Le système est écrit en PHP en utilisant le framework Yii2. Initialement, les rapports ont été créés via la bibliothèque PhpSpreadsheet, qui a remplacé PhpExcel, obsolète de longue date.
Parmi les différents types de rapports, il y en avait un très grand - en fait, l'ensemble complet de toutes les données stockées dans la base de données devrait être téléchargé dans une table Excel. Au stade initial, il n'y a eu aucun problème, mais lorsque le volume a commencé à dépasser plusieurs centaines de milliers d'enregistrements, le script de formation de déchargement a commencé à tomber à la limite de temporisation.
Pour commencer, nous avons relevé cette limite et avons commencé à chercher des moyens de résoudre le problème. Mais une solution temporaire n'a pas duré longtemps - le problème avec la limite de temps s'est transformé en un problème avec la limite de mémoire. Ils ont jeté la «RAM» sur le serveur et supprimé le memory_limit pour cette opération particulière. Très vite, les utilisateurs ont recommencé à se plaindre des erreurs d'exécution. J'ai dû supprimer le délai pour le rapport complet. Mais s'asseoir et regarder une dizaine de minutes sur l'écran avec un indicateur de chargement n'est pas très amusant. De plus, un rapport était parfois nécessaire «ici et maintenant», et chaque minute consacrée à sa formation s'est avérée critique. Les expériences avec les paramètres d'environnement ont été arrêtées, ont gratté l'arrière de la tête et ont commencé à optimiser le code.
Rechercher une solution
La première chose qui a été faite est que le script de rapport a été placé dans le processus d'arrière-plan et que l'utilisateur surveille la progression via la "barre de progression". L'exécution du travail en arrière-plan a été implémentée via le mécanisme de file d'attente à l'aide de Redis pour le stockage. Le travail dans le système ne s'arrête pas, vous pouvez effectuer d'autres tâches et revenir périodiquement à la page du rapport pour voir si le fichier est prêt. Dès que le fichier est formé, l'utilisateur se voit proposer un lien de téléchargement. Mais, comme mentionné ci-dessus, le fichier était parfois requis "immédiatement", et une utilisation accrue ne résolvait pas ce problème. Entre-temps, la quantité de données a continué de croître et le temps nécessaire à la création du fichier a atteint 79 minutes! Ceci est totalement inacceptable, d'autant plus que le reporting est l'un des fondements de la fonctionnalité de ce système. Non, toutes les autres pièces ont fonctionné comme sur des roulettes, mais cette mouche dans la pommade a gâché l'impression générale.
Premiers résultats
Nous nous sommes assis à nouveau pour l'analyse du code. La première chose qui a été testée a été le processus de sélection des données dans la base de données. Mais les requêtes ont déjà été optimisées au maximum. Bien que la demande la plus longue ait été un terrible échantillon avec cinq ou six appels à la monstrueuse FIAS, elle a fonctionné en 2 à 5 secondes. Le point faible n'était pas lui, mais la formation du dossier "exelnik". Des tentatives ont commencé pour optimiser ce processus. À partir de la mise en cache dans redis, aux perversions, telles que la formation de petits "excels" séparés dans des flux parallèles, suivis du collage dans un fichier. Mais le résultat était toujours le même: le problème au fil du temps s'est transformé en un problème de mémoire et vice versa. Il n'y avait pas de terrain d'entente, ne circulant que d'un extrême à l'autre. Après une certaine quantité de données, la consommation de ressources de la bibliothèque a commencé à croître de façon exponentielle et il n'a pas été possible de la vaincre. PhpSpreadsheet - ne convient pas aux fichiers volumineux. En conséquence, il a été décidé de changer de bibliothèque. En option - écrire votre propre analogue pour la formation d'ex-fichiers.
Analyse et sélection d'outils
Ils ne se sont pas précipités pour écrire des vélos, mais pour commencer, ils ont analysé les solutions existantes. Parmi les options possibles, seule la boîte / le bec était d'intérêt. Réécriture rapide du module à l'aide de cette bibliothèque. En conséquence, un rapport complet a été obtenu en 145 secondes. Permettez-moi de vous rappeler que les derniers tests avec PhpSpreadsheet durent 79 minutes, et ici 2,5 minutes! Tests effectués: augmentation de la quantité de données de 2 fois. Le rapport a été généré en 172 secondes. La différence est incroyable. Bien sûr, la bibliothèque n'a pas toutes les mêmes fonctions que PhpSpreadsheet, mais dans ce cas, le minimum d'outils est suffisant, car la vitesse est critique.
Extension pour Yii2
La décision finale a été présentée comme une extension pour Yii2. Peut-être que quelqu'un vous sera utile. L'extension vous permet de télécharger n'importe quel ensemble de données depuis GridView pour exceller tout en maintenant le filtrage et le tri. Il utilise yii / queue et box / spout comme dépendances. Il est logique d'utiliser l'extension pour former de très gros fichiers, enfin, au moins 50000 lignes =) À l'heure actuelle, le module, qui est devenu la base de l'extension, fait face à une charge de près de 600000 lignes.
Lien vers github:
extension Yii2 ExcelReportMerci de votre attention!