Dans cet article, vous découvrirez les requêtes N 1, comment les détecter avec AppSignal et comment les corriger pour accélérer considérablement vos applications Django.
Nous commencerons par les aspects théoriques puis passerons aux exemples pratiques. Les exemples pratiques refléteront des scénarios que vous pourriez rencontrer dans un environnement de production.
Commençons !
Le problème de requête N 1 est un problème de performances répandu dans les applications Web qui interagissent avec une base de données. Ces requêtes peuvent provoquer des goulots d'étranglement importants, qui s'intensifient à mesure que votre base de données se développe.
Le problème se produit lorsque vous récupérez une collection d'objets, puis accédez aux objets associés pour chaque élément de la collection. Par exemple, récupérer une liste de livres nécessite une seule requête (1 requête), mais accéder à l'auteur pour chaque livre déclenche une requête supplémentaire pour chaque élément (N requêtes).
Les problèmes N 1 peuvent également survenir lors de la création ou de la mise à jour de données dans une base de données. Par exemple, parcourir une boucle pour créer ou mettre à jour des objets individuellement, plutôt que d'utiliser des méthodes telles que Bulk_create() ou Bulk_update(), peut entraîner des requêtes excessives.
Les requêtes N 1 sont très inefficaces, car l'exécution de nombreuses petites requêtes est nettement plus lente et plus gourmande en ressources que la consolidation des opérations en moins de requêtes plus volumineuses.
Le comportement QuerySet par défaut de Django peut entraîner par inadvertance des problèmes N 1, surtout si vous ne savez pas comment fonctionnent les QuerySets. Les ensembles de requêtes dans Django sont paresseux, ce qui signifie qu'aucune requête de base de données n'est exécutée tant que le QuerySet n'est pas évalué.
Assurez-vous d'avoir :
Remarque : Le code source de ce projet se trouve dans le référentiel GitHub appsignal-django-n-plus-one.
Nous travaillerons avec une application Web de gestion de livres. L'application Web est conçue pour démontrer le problème de requête N 1 et comment le résoudre.
Commencez par cloner la branche de base du dépôt GitHub :
$ git clone git@github.com:duplxey/appsignal-django-n-plus-one.git \ --single-branch --branch base && cd appsignal-django-n-plus-one
Ensuite, créez et activez un environnement virtuel :
$ python3 -m venv venv && source venv/bin/activate
Installez la configuration requise :
(venv)$ pip install -r requirements.txt
Migrer et remplir la base de données :
(venv)$ python manage.py migrate (venv)$ python manage.py populate_db
Enfin, démarrez le serveur de développement :
(venv)$ python manage.py runserver
Ouvrez votre navigateur Web préféré et accédez à http://localhost:8000/books. L'application Web doit renvoyer une liste JSON de 500 livres de la base de données.
Le site d'administration de Django est accessible à http://localhost:8000/admin. Les informations d'identification de l'administrateur sont :
user: username pass: password
Pour installer AppSignal sur votre projet Django, suivez la documentation officielle :
Assurez-vous que tout fonctionne en redémarrant le serveur de développement :
$ git clone git@github.com:duplxey/appsignal-django-n-plus-one.git \ --single-branch --branch base && cd appsignal-django-n-plus-one
Votre application devrait automatiquement envoyer une erreur de démonstration à AppSignal. À partir de ce moment, toutes vos erreurs seront envoyées à AppSignal. De plus, AppSignal surveillera les performances de votre application et détectera tout problème.
La condition préalable à la résolution des requêtes N 1 est de comprendre le schéma de base de données de votre application. Portez une attention particulière aux relations de vos modèles : ils peuvent vous aider à identifier les problèmes potentiels N1.
L'application Web comporte deux modèles : Auteur et Livre - qui partagent une relation un-à-plusieurs (1:M). Cela signifie que chaque livre est associé à un seul auteur, tandis qu'un auteur peut être lié à plusieurs livres.
Les deux modèles ont une méthode to_dict() pour sérialiser les instances de modèle en JSON. De plus, le modèle Book utilise une sérialisation approfondie (sérialisant le livre ainsi que l'auteur du livre).
Les modèles sont définis dans books/models.py :
$ python3 -m venv venv && source venv/bin/activate
Ils sont ensuite inscrits sur le site d'administration de Django dans books/admin.py, comme ceci :
(venv)$ pip install -r requirements.txt
Notez qu'AuthorAdmin utilise BookInline pour afficher les livres de l'auteur dans la page d'administration de l'auteur.
L'application Web fournit les points de terminaison suivants :
Les liens ci-dessus sont cliquables si le serveur Web de développement est en cours d'exécution.
Et ils sont définis dans books/views.py comme ceci :
(venv)$ python manage.py migrate (venv)$ python manage.py populate_db
Super, vous savez maintenant comment fonctionne l'application web !
Dans la section suivante, nous comparerons notre application pour détecter les requêtes N 1 avec AppSignal, puis modifierons le code pour les éliminer.
La détection des problèmes de performances avec AppSignal est simple. Tout ce que vous avez à faire est d'utiliser/tester l'application comme vous le feriez normalement (par exemple, effectuer des tests auprès des utilisateurs finaux en visitant tous les points de terminaison et en validant les réponses).
Lorsqu'un point de terminaison est atteint, AppSignal crée un rapport de performances pour celui-ci et regroupe toutes les visites associées. Chaque visite sera enregistrée sous forme d'échantillon dans le rapport du point final.
Tout d'abord, visitez tous les points de terminaison de votre application pour générer les rapports de performances :
Ensuite, utilisons le tableau de bord AppSignal pour analyser les points de terminaison lents.
Accédez à votre application AppSignal et sélectionnez Performances > Liste des problèmes dans la barre latérale. Cliquez ensuite sur Moyen pour trier les problèmes par temps de réponse moyen décroissant.
Cliquez sur le point de terminaison le plus lent (books/) pour afficher ses détails.
En regardant le dernier échantillon, nous pouvons voir que ce point de terminaison renvoie une réponse en 1 090 millisecondes. La répartition des groupes montre que SQLite prend 651 millisecondes tandis que Django en prend 439.
Cela indique un problème car un point de terminaison aussi simple que celui-ci ne devrait pas prendre autant de temps.
Pour obtenir plus de détails sur ce qui s'est passé, sélectionnez Échantillons dans la barre latérale, puis le dernier échantillon.
Faites défiler jusqu'à la Chronologie des événements pour voir quelles requêtes SQL ont été exécutées.
Le survol du texte query.sql affiche la requête SQL réelle.
Plus de 1000 requêtes ont été exécutées :
$ git clone git@github.com:duplxey/appsignal-django-n-plus-one.git \ --single-branch --branch base && cd appsignal-django-n-plus-one
C'est un signe clair des requêtes N 1. La première requête a récupéré un livre (1), et chaque requête suivante a récupéré les détails de l'auteur du livre (N).
Pour résoudre ce problème, accédez à books/views.py et modifiez book_list_view() comme ceci :
$ python3 -m venv venv && source venv/bin/activate
En utilisant la méthode select_rated() de Django, nous sélectionnons les données d'objet associées supplémentaires (c'est-à-dire l'auteur) dans la requête initiale. L'ORM exploitera désormais une jointure SQL et la requête finale ressemblera à ceci :
(venv)$ pip install -r requirements.txt
Attendez que le serveur de développement redémarre et testez à nouveau le point de terminaison concerné.
Après une nouvelle analyse comparative, le temps de réponse passe de 1090 à 45 et le nombre de requêtes diminue de 1024 à 2. Il s'agit d'une amélioration de 24x et 512x, respectivement.
Ensuite, regardons le deuxième point de terminaison le plus lent (livres/par-auteurs/).
Utilisez le tableau de bord comme nous l'avons fait à l'étape précédente pour inspecter les requêtes SQL du point de terminaison. Vous remarquerez un modèle N 1 similaire mais moins sévère avec ce point final.
Les performances de ce point de terminaison sont moins sévères car Django est suffisamment intelligent pour mettre en cache les requêtes SQL fréquemment exécutées, c'est-à-dire récupérer à plusieurs reprises l'auteur d'un livre. Consultez la documentation officielle pour en savoir plus sur la mise en cache Django.
Utilisons prefetch_rated() dans books/views.py pour accélérer le point de terminaison :
$ git clone git@github.com:duplxey/appsignal-django-n-plus-one.git \ --single-branch --branch base && cd appsignal-django-n-plus-one
Dans la section précédente, nous avons utilisé la méthode select_rated() pour gérer une relation un-à-un (chaque livre a un seul auteur). Cependant, dans ce cas, nous gérons une relation un-à-plusieurs (un auteur peut avoir plusieurs livres), nous devons donc utiliser prefetch_rated().
La différence entre ces deux méthodes est que select_rated() fonctionne au niveau SQL, tandis que prefetch_rated() optimise au niveau Python. Cette dernière méthode peut également être utilisée pour les relations plusieurs-à-plusieurs.
Pour plus d'informations, consultez la documentation officielle de Django sur prefetch_rated().
Après benchmarking, le temps de réponse passe de 90 à 44 millisecondes, et le nombre de requêtes diminue de 32 à 4.
La découverte des requêtes N 1 dans le site d'administration de Django fonctionne de la même manière.
Tout d'abord, connectez-vous à votre site d'administration et générez des rapports de performances (par exemple, créez quelques auteurs ou livres, mettez-les à jour et supprimez-les).
Ensuite, accédez au tableau de bord de votre application AppSignal, en filtrant cette fois les problèmes par administrateur :
Dans mon cas, les deux points de terminaison les plus lents sont :
Nous ne pouvons pas faire grand-chose avec /admin/login, puisqu'il est entièrement géré par Django, alors concentrons-nous sur le deuxième point de terminaison le plus lent. L'inspecter révélera un problème de requête N 1. L'auteur est récupéré séparément pour chaque livre.
Pour résoudre ce problème, remplacez get_queryset() dans BookInline pour récupérer les détails de l'auteur dans la requête initiale :
$ python3 -m venv venv && source venv/bin/activate
Analysez à nouveau et vérifiez que le nombre de requêtes a diminué.
Dans cet article, nous avons discuté de la détection et de la correction des requêtes N 1 dans Django à l'aide d'AppSignal.
Tirer parti de ce que vous avez appris ici peut vous aider à accélérer considérablement vos applications Web Django.
Les deux méthodes les plus essentielles à garder à l'esprit sont select_rated() et prefetch_rated(). Le premier est utilisé pour les relations un-à-un, et le second pour les relations un-à-plusieurs et plusieurs-à-plusieurs.
Bon codage !
P.S. Si vous souhaitez lire les articles Python dès leur sortie de presse, abonnez-vous à notre newsletter Python Wizardry et ne manquez jamais un seul article !
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!