2008-09-19 6 views
28

Ich habe einige benutzerdefinierte DAO-ähnliche Klassen entwickelt, um einige sehr spezielle Anforderungen für mein Projekt zu erfüllen, bei dem es sich um einen serverseitigen Prozess handelt, der nicht in einem Framework ausgeführt wird.Was ist die beste Lösung für das Datenbankverbindungs-Pooling in Python?

Die Lösung funktioniert gut, außer dass jedes Mal, wenn eine neue Anfrage gemacht wird, öffne ich eine neue Verbindung über MySQLdb.connect.

Was ist die beste "Drop-In" -Lösung, um dies auf das Verbindungs-Pooling in Python umzustellen? Ich stelle mir so etwas wie die Common-DBCP-Lösung für Java vor.

Der Prozess läuft lange und hat viele Threads, die Anfragen stellen müssen, aber nicht alle zur gleichen Zeit ... speziell tun sie eine Menge Arbeit vor kurzen Ausbrüchen von einem Stück ihrer Ergebnisse zu schreiben.

hinzufügen Editiert: Nachdem einige weitere Suche ich anitpool.py gefunden, die anständig aussieht, aber wie ich relativ neu bin Python Ich glaube, ich möchte nur sicherstellen, dass ich kein offensichtlicher/mehr idiomatische/besser fehlt Lösung.

Antwort

15

IMO, die "offensichtliche/mehr idiomatische/bessere Lösung" ist eher ein vorhandenes ORM zu verwenden, als DAO-Klassen wie erfinden .

Es scheint mir, dass ORMs beliebter als "rohe" SQL-Verbindungen sind. Warum? Weil Python ist OO, und die Zuordnung von SQL-Zeile zu Objekt ist unbedingt erforderlich. Es gibt nicht viele Fälle, in denen Sie mit SQL-Zeilen arbeiten, die Python-Objekten nicht zugeordnet sind.

Ich denke, dass oder SQLObject (und die damit verbundene Verbindung Pooling) die idiomatische Pythonic Lösung.

Pooling als separates Feature ist nicht sehr häufig, da reines SQL (ohne Objekt-Mapping) für die Art komplexer, lang laufender Prozesse, die vom Verbindungs-Pooling profitieren, nicht sehr beliebt ist. Ja, reines SQL wird verwendet, aber es wird immer in einfacheren oder kontrollierteren Anwendungen verwendet, in denen das Pooling nicht hilfreich ist.

Ich glaube, Sie zwei Alternativen haben könnten:

  1. Revise Ihre Klassen SQLAlchemy oder SQLObject zu verwenden. Während dies zunächst schmerzhaft erscheint [all diese Arbeit wird verschwendet], sollten Sie in der Lage sein, all das Design und das Denken zu nutzen, und es ist lediglich eine Übung, eine weit verbreitete ORM- und Pooling-Lösung zu übernehmen.
  2. Rollen Sie Ihren eigenen einfachen Verbindungspool mit dem von Ihnen beschriebenen Algorithmus - einem einfachen Set oder einer Liste von Verbindungen, die Sie durchlaufen.
15

Wickeln Sie Ihre Verbindungsklasse.

Legen Sie eine Grenze fest, wie viele Verbindungen Sie herstellen. Geben Sie eine nicht verwendete Verbindung zurück. Abfangen nah, um die Verbindung freizugeben.

Update: ich so etwas wie dies in dbpool.py setzen:

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

Chris, hat sicherlich jemand das schon gebaut? Im schlimmsten Fall kann ich es selbst schreiben, aber offensichtlich sollte dies eine ziemlich häufige Anforderung für Leute sein, die keine bestehenden ORMs/Frameworks verwenden, und ich bin sicher, dass jemand anderes bereits eine Lösung entwickelt hat, die sich im Laufe der Zeit bewährt hat. – John

+0

Ich habe das schon einmal mit Oracle gemacht, und ich glaube, es waren insgesamt weniger als 50 Codezeilen beteiligt. Verwenden Sie im Grunde eine ID, ein Wörterbuch, speichern Sie die Verbindung, speichern Sie den Verwendungsstatus usw. Sehr einfach? – Chris

+3

@Chris, durch diese Kette der Logik, sollte ich beginnen, meine hashmaps und Listen auch selbst zu implementieren. –

21

In MySQL?

Ich würde sagen, nicht mit dem Verbindungspooling stören.Sie sind oft eine Quelle von Problemen und mit MySQL bringen sie Ihnen nicht den Leistungsvorteil, auf den Sie hoffen. Dieser Weg mag politisch sehr anstrengend sein, denn es gibt so viele Best Practices, die in diesem Raum über die Vorteile von Connection-Pooling winken.

Verbindungspools sind einfach eine Brücke zwischen der Post-Web-Ära staatenloser Anwendungen (z. B. HTTP-Protokoll) und der Prä-Web-Ära von statusbehafteten, langlebigen Batch-Verarbeitungsanwendungen. Da Verbindungen in Pre-Web-Datenbanken sehr teuer waren (da sich niemand zu lange damit beschäftigt hat, wie lange eine Verbindung aufgebaut werden musste), entwickelten Post-Web-Anwendungen dieses Verbindungspool-Schema so, dass bei jedem Treffer dieser enorme Verarbeitungsaufwand nicht auftrat auf dem RDBMS.

Da MySQL eher ein RDBMS der Web-Ära ist, sind Verbindungen extrem leicht und schnell. Ich habe viele High-Volume-Webanwendungen geschrieben, die überhaupt keinen Verbindungspool für MySQL verwenden.

Dies ist eine Komplikation, auf die Sie verzichten können, solange Sie kein politisches Hindernis überwinden müssen.

+10

8 Jahre nachdem diese Antwort veröffentlicht wurde und das Pooling weiterhin relevant bleibt. Wenn Sie eine Webanwendung mit hohem Datenaufkommen ausführen, können Sie unabhängig von der Statuslosigkeit problemlos auf das Limit "Zu viele Verbindungen" zugreifen. Ein Pool wird dazu beitragen, dies zu mildern, indem er auf eine freie Verbindung wartet, anstatt hart zu versagen. Wenn Sie den App-Server horizontal skalieren möchten, wird Ihre Datenbank wahrscheinlich nicht auf demselben Computer ausgeführt. In diesem Fall möchten Sie wahrscheinlich über HTTPS eine Verbindung zu ihm herstellen, was zu einem erheblichen Overhead führt. Ein Pool wird auch hier helfen. – Joe

3

Ihr eigene Verbindung gefunden Pool ist eine schlechte Idee, wenn Ihre Anwendung je Multi-Threading verwenden zu beginnen entscheidet. Das Erstellen eines Verbindungspools für eine Multithread-Anwendung ist viel komplizierter als der für eine Single-Thread-Anwendung. Sie können in diesem Fall etwas wie PySQLPool verwenden.

Es ist auch eine schlechte Idee, ein ORM zu verwenden, wenn Sie nach Leistung suchen.

Wenn Sie mit riesigen/schweren Datenbanken zu tun haben, die viele Selects verarbeiten müssen, fügt Updates und löscht gleichzeitig, dann werden Sie Leistung benötigen, was bedeutet, dass Sie benutzerdefinierte benötigen SQL wurde geschrieben, um Lookups und Sperrzeiten zu optimieren. Mit einem ORM haben Sie normalerweise nicht diese Flexibilität.

Also im Grunde, yeah, Sie können Ihren eigenen Verbindungspool erstellen und ORMs verwenden, aber nur, wenn Sie sicher sind, dass Sie nichts von dem benötigen, was ich gerade beschrieben habe.

5

Alt Faden, sondern für allgemeine Zwecke Pooling (Verbindungen oder jedes teures Objekt), verwende ich so etwas wie:

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 

Welche lazily konstruiert, verfügt über eine optionale Grenze und soll auf jeden Anwendungsfall verallgemeinert I kann sich vorstellen. Natürlich setzt dies voraus, dass Sie wirklich das Pooling jeder Ressource benötigen, die Sie für viele moderne SQL-Likes nicht benötigen. Verbrauch:

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

Dies davon ausgehen, dass jedes Objekt Ctor schafft eine angemessene destructor hat, wenn nötig (einige Server nicht töten Verbindungsobjekte, wenn sie geschlossen sind explizit).

+0

Sie haben zwei Dinge vergessen: 1. 'Yield i' kann eine Exception auslösen, also sollten Sie es mit try ... excepten. 2. 'lpool.put (i)' kann Objekt im falschen Zustand zurückgeben (wie DB-Verbindung mit geöffneter Transaktion) – socketpair

+0

Die Ausnahme, die liefert, sollte tatsächlich vom Kontextmanager gehandhabt werden. Unabhängig davon, wie der Kontext beendet wird (Ausnahme oder nicht), wird der Rest der Funktion ausgeführt. Aber ja, wenn Sie stateful Manipulationen an der db machen, wäre die Handhabung im Post-Yield-Bit der Funktion eine gute Idee. – metaperture

+0

In der Praxis ist es wahrscheinlich besser, das Pool-Objekt in Chris 'bearbeitetem Beitrag zu verwenden, aber für diejenigen, die lernen möchten, Pools allgemein zu implementieren, halte ich das für ein schönes Beispiel. – metaperture

1

Auf einen alten Thread antwortend, aber das letzte Mal, als ich überprüft habe, bietet MySQL Verbindungspooling als Teil seiner Treiber an.

Sie können sie überprüfen unter:

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

Von TFA, Angenommen, Sie explizit eine Verbindung Pool öffnen möchten (wie OP angegeben hatte):

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

Auf diesen Pool wird dann zugegriffen, indem vom Pool aus über die Funktion get_connection() angefordert wird.

cnx1 = cnxpool.get_connection() 
cnx2 = cnxpool.get_connection() 
Verwandte Themen