De temps en temps, j'exécute des requêtes brutes en utilisant connection.cursor()
au lieu d'utiliser ORM (car ce n'est certainement pas une solution miracle).
J'ai remarqué qu'à plusieurs endroits, je n'appelle pas explicitement cursor.close()
après avoir terminé avec la base de données. Jusqu'à présent, cela n'a entraîné aucune erreur ni problème de performances. Je me demande quel genre de problèmes pourrais-je avoir sans fermer le curseur explicitement, qu'est-ce qui peut mal tourner?
Pour autant que je sache, connection
et cursor
dans Django suivez "Python Database API Specification v2.0" ( PEP-249 =). Et, selon lui, cursor
serait automatiquement fermé chaque fois que la méthode __del__()
est appelée. Je suppose que la question pourrait être aussi: Y a-t-il un cas d'utilisation quand il n'est pas appelé?
Pour info, j'utilise Python 2.7 et Django 1.6.5.
__del__
/.close()
:
__del__
N'est pas garanti d'être appelé__del__
(mauvaise pratique, mais vrai)Sur les connexions serveur en général
La plupart des serveurs ont une propriété de configuration de délai d'inactivité (appelons cela T). Si une connexion est inactive pendant plus de T secondes, le serveur supprimera la connexion. La plupart des serveurs ont également des propriétés pour définir la taille du pool de threads de travail (W). Si vous disposez déjà de connexions W à votre serveur, il se bloquera probablement lorsqu'une nouvelle connexion sera tentée. Imaginez une seconde que vous n'avez pas la possibilité de fermer explicitement les connexions. Dans ce cas, vous devez définir un délai d'expiration suffisamment petit pour que votre pool de travail ne soit jamais complètement utilisé, en fonction du nombre de connexions simultanées dont vous disposez.
Cependant, si vous fermez vos curseurs/connexions (même s'ils ne sont pas équivalents par [3] ci-dessus, ils se comportent de la même manière), vous n'avez pas à gérer ces propriétés de configuration du serveur et votre pool de threads doit simplement être grand assez pour gérer toutes les connexions simultanées (avec l'option pour une attente occasionnelle de nouvelles ressources). J'ai vu certains serveurs (par exemple Titan sur Cassandra) incapables de se remettre de manquer de travailleurs dans le pool de threads, donc tout le serveur tombe en panne jusqu'au redémarrage.
TL/DR Si vous utilisez des bibliothèques très bien développées, comme celles mentionnées par dano
, vous n'aurez pas de problème. Si vous utilisez des bibliothèques moins vierges, vous pouvez finir par bloquer sur le serveur l'acquisition d'un thread de travail si vous n'appelez pas .close()
, selon la configuration de votre serveur et les taux d'accès.
La classe cursor
de Django n'est qu'un wrapper autour du cursor
de la base de données sous-jacente, donc l'effet de laisser le cursor
ouvert est essentiellement lié au pilote de base de données sous-jacent.
Selon psycopg2 (psycopg2 est le pilote de base de données Django utilise pour les bases de données PostgreSQL) FAQ , leurs curseurs sont légers, mais ils seront mis en cache les données renvoyées par les requêtes que vous avez faites à l'aide de l'objet curseur, ce qui pourrait potentiellement gaspiller la mémoire:
Les curseurs sont des objets légers et leur création ne devrait pas poser de problème. Mais notez que les curseurs utilisés pour récupérer les jeux de résultats mettront en cache les données et utiliseront la mémoire proportionnellement à la taille du jeu de résultats. Notre suggestion est de créer presque toujours un nouveau curseur et de supprimer les anciens dès que les données ne sont plus nécessaires (appelez close () sur eux.) La seule exception est les boucles serrées où l'on utilise généralement le même curseur pour tout un tas de INSÉRER ou METTRE À JOUR.
Django utilise MySQLdb
comme backend pour MySQL, qui a plusieurs types de curseurs, dont certains qui stockent réellement leurs jeux de résultats côté serveur. La documentation MySQLdb
pour Cursor.close
fait remarquer qu'il est très important de fermer le curseur côté serveur lorsque vous avez terminé avec eux:
Si vous utilisez des curseurs côté serveur, il est très important de fermer le curseur lorsque vous en avez terminé et avant d'en créer un nouveau.
Cependant, cela n'est pas pertinent pour Django, car il utilise la classe Cursor
par défaut fournie par MySQLdb
, qui stocke les résultats côté client. Laisser un curseur utilisé ouvert risque de gaspiller la mémoire utilisée par le jeu de résultats stocké, tout comme psycopg2
. La méthode close
sur le curseur supprime simplement la référence interne à la connexion db et épuise le jeu de résultats stocké:
def close(self):
"""Close the cursor. No further queries will be possible."""
if not self.connection: return
while self.nextset(): pass
self.connection = None
Autant que je sache en regardant leur source, tous les backends restants utilisés par Django ( cx_Oracle , sqlite / pysqlite2 ) tous suivent le même schéma; libérez de la mémoire en supprimant/réinitialisant les résultats/références d'objets stockés. Les documentation sqlite ne mentionnent même pas que la classe Cursor
a une méthode close, et elle n'est utilisée que sporadiquement dans l'exemple de code inclus.
Vous avez raison de dire qu'un cursor
sera fermé lorsque __del__()
est appelé sur l'objet cursor
, donc la nécessité de fermer explicitement n'est un problème que si vous gardez un référence longue durée au cursor
; par exemple. un objet self.cursor
que vous conservez comme méthode d'instance d'une classe.
L'appel explicite de cursor.close()
peut être dû à deux raisons:
__del__
n'est pas garanti d'être appelé et a quelques problèmes que vous pouvez lire ici et iciJe suis un peu en retard à cette question. Peut-être que vous voulez une portée de sortie proche.
from contextlib import closing
from Django.db import connection
with closing(connection.cursor()) as cursor:
cursor.execute(...)
cursor.execute(...)
cursor.execute(...)
Bien que le système d'exploitation puisse normalement être utilisé pour libérer des ressources, il est toujours judicieux de fermer des choses comme les connexions à la base de données pour garantir que les ressources sont libérées lorsqu'elles ne sont plus nécessaires, la chose vraiment importante du point de vue de la base de données est de s'assurer que toutes les modifications sont commit()
éd.