2008-09-19 5 views
28

J'ai développé des classes de type DAO personnalisées pour répondre à des exigences très spécifiques pour mon projet qui est un processus côté serveur qui ne fonctionne pas dans n'importe quel type de framework.Quelle est la meilleure solution pour le regroupement de connexions de bases de données en python?

La solution fonctionne très bien, sauf que chaque fois qu'une nouvelle demande est faite, j'ouvre une nouvelle connexion via MySQLdb.connect.

Quelle est la meilleure solution "drop in" pour passer à l'utilisation du pooling de connexions en python? J'imagine quelque chose comme la solution commune DBCP pour Java.

Le processus est long et a beaucoup de threads qui ont besoin de faire des demandes, mais pas tous en même temps ... en particulier, ils font beaucoup de travail avant de brèves rafales d'écrire une partie de leurs résultats.

Sous la direction d'ajouter: Après un peu plus de recherche que j'ai trouvé anitpool.py qui semble décent, mais comme je suis relativement nouveau pour que je python suppose que je veux juste vous assurer que je ne manque pas un plus évident/plus idiomatiques/mieux Solution.

Répondre

15

OMI, la « plus évidente/solution plus idiomatiques/mieux » est d'utiliser un ORM existant plutôt que d'inventer des classes DAO comme .

Il me semble que les ORM sont plus populaires que les connexions SQL "brutes". Pourquoi? Parce que Python est OO, et le mappage de la ligne SQL à l'objet est absolument essentiel. Il n'y a pas beaucoup de cas où vous traitez des lignes SQL qui ne correspondent pas aux objets Python.

Je pense que SQLAlchemy ou SQLObject (et la mise en commun de la connexion associée) plus la solution Pythonic idiomatique.

La mise en pool en tant que fonctionnalité distincte n'est pas très courante car le SQL pur (sans mappage d'objet) n'est pas très populaire pour le type de processus complexes et de longue durée qui bénéficient du pool de connexions. Oui, le code SQL pur est utilisé, mais il est toujours utilisé dans des applications plus simples ou plus contrôlées où la mise en commun n'est pas utile.

Je pense que vous pourriez avoir deux alternatives:

  1. Révisez vos cours à utiliser SQLAlchemy ou SQLObject. Bien que cela semble douloureux au début [tout ce travail gaspillé], vous devriez être en mesure de tirer parti de toute la conception et la pensée et c'est simplement un exercice dans l'adoption d'une solution ORM et pooling largement utilisé. Faites rouler votre propre pool de connexions simples en utilisant l'algorithme que vous avez décrit - un simple ensemble ou une liste de connexions que vous parcourez.
15

Enveloppez votre classe de connexion.

Définissez une limite sur le nombre de connexions que vous effectuez. Renvoyer une connexion inutilisée. Interception à proximité pour libérer la connexion.

Mise à jour: je mets quelque chose comme ça dans dbpool.py:

import sqlalchemy.pool as pool 
import MySQLdb as mysql 
mysql = pool.manage(mysql) 
+2

Chris, sûrement quelqu'un a déjà construit ça? Dans le pire des cas, je peux l'écrire moi-même mais cela devrait être une exigence assez courante pour les personnes n'utilisant pas les ORM/frameworks existants, et je suis sûr que quelqu'un d'autre a déjà créé une solution éprouvée au fil du temps. – John

+0

Je l'ai fait avant, avec Oracle, et je pense qu'il implique moins de 50 lignes de code, au total. Fondamentalement, utilisez un identifiant, un dictionnaire, stockez la connexion, stockez le statut d'utilisation, etc. Très simple? – Chris

+3

@Chris, par cette chaîne de logique, je devrais commencer à implémenter mes hashmaps et mes listes par moi-même. –

21

En MySQL?

Je dirais ne pas déranger avec la mise en commun de la connexion.Ils sont souvent une source de problèmes et avec MySQL, ils ne vous apporteront pas l'avantage de performance que vous espérez. Cette route peut être beaucoup d'efforts à suivre - politiquement - parce qu'il y a tellement de bonnes pratiques à la main et de verbiage dans cet espace sur les avantages de la mise en commun des connexions. Les pools de connexions sont simplement un pont entre l'ère post-web des applications sans état (par exemple le protocole HTTP) et l'ère pré-web des applications de traitement par lots à état dynamique de longue durée. Étant donné que les connexions étaient très coûteuses dans les bases de données pré-web (puisque personne ne se préoccupait trop de la durée de la connexion), les applications post-web ont conçu ce schéma de pool de connexions. sur le SGBDR. Depuis MySQL est plus un SGBDR web-ère, les connexions sont extrêmement léger et rapide. J'ai écrit de nombreuses applications Web à haut volume qui n'utilisent pas du tout un pool de connexion pour MySQL.

Il s'agit d'une complication dont vous pouvez tirer profit, tant qu'il n'y a pas d'obstacle politique à surmonter.

+10

8 ans après cette réponse a été publiée et mise en commun continue de rester pertinent. Si vous exécutez une application Web avec un trafic important, vous pouvez facilement accéder à la limite "Trop de connexions", indépendamment de son état d'apatridie. Un pool aidera à atténuer cela en attendant une connexion gratuite au lieu de hard-failing. , Si vous souhaitez également à l'échelle de votre serveur d'applications horizontalement, votre base de données ne va probablement pas vivre sur la même machine. Dans ce cas, vous voudrez probablement vous connecter à HTTPS, ce qui a un coût important. Une piscine va aider ici aussi. – Joe

3

Faire votre propre pool de connexion est une mauvaise idée si votre application décide jamais de commencer à utiliser le multi-threading. Créer un pool de connexions pour une application multithread est beaucoup plus compliqué que pour une application monothread. Vous pouvez utiliser quelque chose comme PySQLPool dans ce cas.

C'est aussi une idée BAD d'utiliser un ORM si vous recherchez des performances.

Si vous traitez avec des bases de données volumineuses/lourdes qui doivent gérer un grand nombre de sélections, d'insertions, de mises à jour et de suppressions en même temps, vous aurez besoin de performances, ce qui signifie que vous aurez besoin de SQL écrit pour optimiser les recherches et les temps de verrouillage. Avec un ORM, vous n'avez généralement pas cette flexibilité. Donc, fondamentalement, oui, vous pouvez créer votre propre pool de connexion et utiliser les ORM, mais seulement si vous êtes sûr que vous n'aurez besoin de rien de ce que je viens de décrire.

5

fil vieux, mais pour des fins générales mise en commun (connexions ou tout objet coûteux), j'utiliser quelque chose comme:

def pool(ctor, limit=None): 
    local_pool = multiprocessing.Queue() 
    n = multiprocesing.Value('i', 0) 
    @contextlib.contextmanager 
    def pooled(ctor=ctor, lpool=local_pool, n=n): 
     # block iff at limit 
     try: i = lpool.get(limit and n.value >= limit) 
     except multiprocessing.queues.Empty: 
      n.value += 1 
      i = ctor() 
     yield i 
     lpool.put(i) 
    return pooled 

qui construit paresseusement, a une limite facultative et devrait se généraliser à tous les cas d'utilisation I peut penser à. Bien sûr, cela suppose que vous ayez vraiment besoin de la mise en commun de n'importe quelle ressource, ce que vous ne pouvez pas faire pour beaucoup de préférences SQL modernes. Utilisation:

# in main: 
my_pool = pool(lambda: do_something()) 
# in thread: 
with my_pool() as my_obj: 
    my_obj.do_something() 

Cela suppose que tout ce qui ne cteur objet crée a un destructor approprié si nécessaire (certains serveurs ne tuent pas des objets de connexion à moins qu'ils ne soient explicitement fermés).

+0

Vous avez oublié deux choses: 1. 'rendement I' peut soulever exception, vous devez envelopper avec try ... except. 2. 'lpool.put (i)' peut renvoyer un objet dans un mauvais état (comme une connexion db avec une transaction ouverte) – socketpair

+0

L'exception générant devrait être gérée par le gestionnaire de contexte. Peu importe la façon dont le contexte est quitté (exception ou non), le reste de la fonction s'exécutera. Mais oui, si vous faites des manipulations avec état sur la base de données, il serait bon de gérer cela dans le bit post-rendement de la fonction. – metaperture

+0

En pratique, l'utilisation de l'objet pool dans le post édité par Chris est probablement meilleure, mais pour ceux qui cherchent à apprendre à implémenter des pools en général, je pense que c'est un bon exemple. – metaperture

1

En répondant à un ancien thread mais la dernière fois que j'ai vérifié, MySQL offre la mise en commun des connexions dans le cadre de ses pilotes.

Vous pouvez les consulter à:

https://dev.mysql.com/doc/connector-python/en/connector-python-connection-pooling.html

De TFA, En supposant que vous souhaitez ouvrir un pool de connexion explicitement (comme OP avait déclaré):

dbconfig = { "database": "test", "user":"joe" } 
cnxpool = mysql.connector.pooling.MySQLConnectionPool(pool_name = "mypool",pool_size = 3, **dbconfig) 

Ce pool est ensuite accédé en demandant à partir du pool via la fonction get_connection().

cnx1 = cnxpool.get_connection() 
cnx2 = cnxpool.get_connection() 
Questions connexes