it-swarm.com.de

Ist ein asynchroner jdbc-Aufruf möglich?

Ich frage mich, ob es eine Möglichkeit gibt, asynchrone Aufrufe einer Datenbank durchzuführen?

Stellen Sie sich zum Beispiel vor, ich habe eine große Anfrage, deren Bearbeitung sehr lange dauert. Ich möchte die Anfrage senden und eine Benachrichtigung erhalten, wenn die Anfrage einen Wert zurückgibt (durch Übergeben eines Listener/Rückrufs oder so). Ich möchte das Warten auf die Antwort der Datenbank nicht blockieren.

Ich denke nicht, dass die Verwendung eines Pools von Threads eine Lösung ist, da sie nicht skaliert wird. Bei großen gleichzeitigen Anforderungen wird dies eine sehr große Anzahl von Threads erzeugen.

Wir stehen vor solchen Problemen mit Netzwerkservern und haben mit dem Systemaufruf select/poll/epoll Lösungen gefunden, um zu vermeiden, dass ein Thread pro Verbindung verwendet wird. Ich frage mich nur, wie man eine ähnliche Funktion mit Datenbankanfragen hat?

Hinweis: Ich bin mir bewusst, dass die Verwendung eines FixedThreadPool eine gute Lösung darstellt, aber ich bin überrascht, dass niemand ein wirklich asynchrones System entwickelt hat (ohne die Verwendung eines zusätzlichen Threads).

** Update **
Aufgrund des Mangels an praktischen Lösungen habe ich mich entschlossen, selbst eine Bibliothek (Teil von finagle) zu erstellen: finagle-mysql . Es dekodiert/dekodiert im Wesentlichen mysql request/response und verwendet Finagle/Netty unter der Haube. Es skaliert auch bei einer großen Anzahl von Verbindungen sehr gut.

141
Steve Gury

Ich verstehe nicht, wie die vorgeschlagenen Ansätze, die JDBC-Aufrufe in Actors, Executors oder irgendetwas anderes einschließen, hier helfen können - kann jemand etwas klären.

Das grundlegende Problem ist sicherlich, dass die JDBC-Vorgänge auf Socket-E/A blockieren. Wenn dies geschieht, wird der Thread am Ende der Story blockiert. Welches Wrapper-Framework auch immer Sie wählen, es wird ein Thread verwendet, der pro gleichzeitiger Anforderung belegt oder blockiert wird.

Wenn die zugrunde liegenden Datenbanktreiber (MySql?) Ein Mittel bieten, um die Socket-Erstellung abzufangen (siehe SocketFactory), könnte ich mir vorstellen, dass es möglich ist, eine asynchrone ereignisgesteuerte Datenbankschicht auf der JDBC-API zu erstellen ganze JDBC hinter einer ereignisgesteuerten Fassade, und diese Fassade würde nicht wie JDBC aussehen (nachdem dies ereignisgesteuert wäre). Die Datenbankverarbeitung würde asynchron auf einem anderen Thread als dem Aufrufer ausgeführt, und Sie müssen herausfinden, wie Sie einen Transaktionsmanager erstellen, der nicht auf der Thread-Affinität beruht.

So etwas wie der von mir erwähnte Ansatz würde es sogar einem einzelnen Hintergrund-Thread ermöglichen, eine Last gleichzeitiger JDBC-Execs zu verarbeiten. In der Praxis würden Sie wahrscheinlich einen Pool von Threads ausführen, um mehrere Kerne zu verwenden.

(Natürlich kommentiere ich nicht die Logik der ursprünglichen Frage, sondern nur die Antworten, die implizieren, dass Parallelität in einem Szenario mit blockierendem Socket IO ohne den Benutzer eines Selektormusters möglich ist - einfacher, nur um das typische zu ermitteln JDBC-Parallelität und fügen Sie einen Verbindungspool mit der richtigen Größe ein.


Sieht aus, als würde MySql wahrscheinlich etwas in der Richtung tun, die ich vorschlage - http://code.google.com/p/async-mysql-connector/wiki/UsageExample

147
johnlon

Es ist nicht möglich, einen asynchronen Aufruf an die Datenbank über JDBC auszuführen, aber Sie können asynchrone Aufrufe an JDBC mit Actors ausführen (z. B. ruft der Akteur über JDBC den DB auf und sendet Nachrichten an Dritte, wenn die Anrufe beendet sind) oder, wenn Sie CPS mögen, mit pipelined Futures (Versprechen) (eine gute Implementierung ist ScalazVersprechen )

Ich denke nicht, dass die Verwendung eines Pools von Threads eine Lösung ist, da sie nicht skaliert wird. Bei großen gleichzeitigen Anforderungen wird dies eine sehr große Anzahl von Threads erzeugen.

Scala-Akteure sind standardmäßig ereignisbasiert (nicht threadbasiert). Mit der Fortsetzungsplanung können Sie Millionen von Schauspielern in einem Standard-JVM-Setup erstellen. 

Wenn Sie auf Java abzielen, ist Akka Framework eine Actor-Modellimplementierung mit einer guten API sowohl für Java als auch für Scala.


Abgesehen davon ist die Synchronität von JDBC für mich absolut sinnvoll. Die Kosten einer Datenbanksitzung sind weit höher als die Kosten für die Blockierung des Java-Threads (entweder im Vordergrund oder Hintergrund) und das Warten auf eine Antwort. Wenn Ihre Abfragen so lange laufen, dass die Fähigkeiten eines Executor-Services (oder das Umschließen von Actor/Fork-Join/Promise Concurrency-Frameworks) nicht ausreichen (und Sie zu viele Threads verwenden), sollten Sie zuerst über Ihre Datenbank laden. Normalerweise kommt die Antwort einer Datenbank sehr schnell zurück, und ein Executor-Service, der mit einem festen Thread-Pool gesichert ist, ist eine gute Lösung. Wenn Sie zu viele lang laufende Abfragen haben, sollten Sie eine Vorverarbeitung (Vor-) Verarbeitung in Betracht ziehen - etwa eine nächtliche Neuberechnung der Daten oder ähnliches.

40
Vasil Remeniuk

Vielleicht könnten Sie ein asynchrones JMS-Messaging-System verwenden, das sich gut skalieren lässt, IMHO:

  • Senden Sie eine Nachricht an eine Warteschlange, in der die Abonnenten die Nachricht annehmen und den SQL-Prozess ausführen. Ihr Hauptprozess läuft weiter und akzeptiert oder sendet neue Anfragen.

  • Wenn der SQL-Prozess beendet ist, können Sie den umgekehrten Weg ausführen: Senden Sie eine Nachricht mit dem Ergebnis des Prozesses an eine ResponseQueue. Ein Listener auf der Clientseite akzeptiert dies und führt den Rückrufcode aus.

11
Tomas Narros

Es gibt keine direkte Unterstützung in JDBC, Sie haben jedoch mehrere Optionen wie MDB, Executors von Java 5.

"Ich denke nicht, dass die Verwendung eines Pools von Threads eine Lösung ist, da er nicht skaliert wird. Bei großen gleichzeitigen Anforderungen wird dies eine sehr große Anzahl von Threads erzeugen."

Ich bin neugierig, warum ein begrenzter Pool von Threads nicht skaliert werden sollte. Es ist ein Pool, der kein Thread-pro-Request ist, um einen Thread für jede Anfrage zu erzeugen. Ich habe dies schon seit geraumer Zeit in einer stark beanspruchten Webapp verwendet und wir haben bisher keine Probleme gesehen.

7

Es scheint, dass eine neue asynchrone jdbc-API "JDBC next" in Arbeit ist.

Siehe Präsentation hier

Sie können die API von hier herunterladen.

4
Sebastien

Wie in anderen Antworten erwähnt, ist die JDBC-API ihrer Natur nach nicht asynchron.
Wenn Sie jedoch mit einer Teilmenge der Operationen und einer anderen API leben können, gibt es Lösungen. Ein Beispiel ist https://github.com/jasync-sql/jasync-sql , das für MySQL und PostgreSQL funktioniert.

4
oshai

Es wird eine Lösung entwickelt, um reaktive Konnektivität mit relationalen Standarddatenbanken zu ermöglichen.

Personen, die skalieren und dabei die Nutzung relationaler Datenbanken beibehalten möchten, sind aufgrund bestehender Standards, die auf der Blockierung von E/A basieren, von der reaktiven Programmierung ausgeschlossen. R2DBC gibt eine neue API an, die reaktiven Code ermöglicht, der effizient mit relationalen Datenbanken zusammenarbeitet.

R2DBC ist eine Spezifikation, die von Grund auf für die reaktive Programmierung mit SQL-Datenbanken entwickelt wurde und ein nicht blockierendes SPI für Datenbank-Treiberimplementierer und Autoren von Client-Bibliotheken definiert. R2DBC-Treiber implementieren das Datenbankverdrahtungsprotokoll vollständig auf einer nicht blockierenden E/A-Schicht.

R2DBC-Website

GitHub von R2DBC

Funktionsmatrix

enter image description here

3
Yassin Hajaj

Eine alte Frage, aber noch mehr Informationen. Es ist nicht möglich, dass JDBC asynchrone Anforderungen an die Datenbank selbst ausgibt, es sei denn, ein Hersteller stellt eine Erweiterung für JDBC und einen Wrapper bereit, mit dem JDBC verarbeitet werden kann. Das heißt, es ist möglich, JDBC selbst mit einer Verarbeitungswarteschlange zu umschließen und eine Logik zu implementieren, die die Warteschlange für eine oder mehrere separate Verbindungen verarbeiten kann. Ein Vorteil davon ist bei einigen Aufruftypen, dass die Logik die Aufrufe bei Verarbeitung unter starker Last in JDBC-Stapel für die Verarbeitung umwandeln kann, was die Logik erheblich beschleunigen kann. Dies ist am nützlichsten für Anrufe, bei denen Daten eingefügt werden, und das tatsächliche Ergebnis muss nur protokolliert werden, wenn ein Fehler vorliegt. Ein gutes Beispiel dafür ist, wenn Einfügungen ausgeführt werden, um Benutzeraktivitäten zu protokollieren. Die Anwendung kümmert sich nicht, wenn der Anruf sofort oder in wenigen Sekunden beendet wird.

Als Randbemerkung bietet ein auf dem Markt erhältliches Produkt einen richtliniengesteuerten Ansatz, um asynchrone Aufrufe wie die von mir beschriebenen asynchron auszuführen ( http://www.heimdalldata.com/ ). Haftungsausschluss: Ich bin Mitbegründer dieser Firma. Auf diese Weise können reguläre Ausdrücke auf Datentransformationsanforderungen angewendet werden, z. B. Einfügen/Aktualisieren/Löschen für eine JDBC-Datenquelle, und sie werden zur Verarbeitung automatisch zusammengefügt. Bei Verwendung mit MySQL und der Option rewriteBatchedStatements ( MySQL und JDBC mit rewriteBatchedStatements = true ) kann dies die Gesamtlast der Datenbank erheblich verringern.

3
Erik Brandsberg

Die Java 5.0-Executoren könnten nützlich sein.

Sie können über eine feste Anzahl von Threads verfügen, um lang andauernde Vorgänge auszuführen. Und anstelle von Runnable können Sie Callable verwenden, die ein Ergebnis zurückgeben. Das Ergebnis wird in einem Objekt Future<ReturnType> eingekapselt, sodass Sie es erhalten können, wenn es wieder verfügbar ist.

3
Bozho

Ajdbc-Projekt scheint dieses Problem zu lösen http://code.google.com/p/adbcj/

Derzeit gibt es zwei experimentelle nativ async-Treiber für mysql und postgresql.

3
Sebastien

Hier ist ein Überblick darüber, wie eine nicht blockierende jdbc-API von Oracle aussehen könnte, die auf JavaOne vorgestellt wurde: https://static.rainfocus.com/Oracle/oow16/sess/1461693351182001EmRq/ppt/CONF1578%2020160916. pdf

Es scheint also, dass am Ende wirklich asynchrone JDBC-Aufrufe tatsächlich möglich sind.

2
nemoo

Sie haben meiner Meinung nach drei Möglichkeiten:

  1. Verwenden Sie eine Concurrent-Queue , um Nachrichten auf eine kleine und feste Anzahl von Threads zu verteilen. Wenn Sie also 1000 Verbindungen haben, haben Sie 4 Threads, nicht 1000 Threads.
  2. Führen Sie den Datenbankzugriff auf einen anderen Knoten (d. H. Einen anderen Prozess oder eine andere Maschine) aus, und veranlassen Sie, dass Ihr Datenbankclient asynchrone Netzwerkaufrufe für diesen Knoten durchführt.
  3. Implementieren Sie ein echtes verteiltes System durch asynchrone Nachrichten. Dafür benötigen Sie eine Messaging-Warteschlange wie CoralMQ oder Tibco.

Diclaimer: Ich bin einer der Entwickler von CoralMQ.

2
rdalmeida

Nur eine verrückte Idee: Sie könnten ein Iterate-Muster über das JBDC-Ergebnis verwenden, das in ein Future/Promise-Paket eingeschlossen ist

Hammersmith macht das für MongoDB .

2
jwinandy

Die commons-dbutils-Bibliothek unterstützt eine AsyncQueryRunner, der Sie eine ExecutorService zur Verfügung stellen, und gibt eine Future zurück. Es lohnt sich, es auszuprobieren, da es einfach zu bedienen ist und sichergestellt ist, dass keine Ressourcen auslaufen.

1
William Speirs

Wenn Sie sich für asynchrone Datenbank-APIs für Java interessieren, sollten Sie wissen, dass es eine neue Initiative gibt, um eine Reihe von Standard-APIs zu entwickeln, die auf CompletableFuture und Lambdas basieren. Es gibt auch eine Implementierung dieser APIs über JDBC, mit der diese APIs geübt werden können: https://github.com/Oracle/oracle-db-examples/tree/master/Java/AoJ Das JavaDoc wird in README des Github-Projekts erwähnt.

1

Ich denke hier nur an Ideen. Warum konnten Sie nicht über einen Pool von Datenbankverbindungen verfügen, von denen jede einen Thread hat. Jeder Thread hat Zugriff auf eine Warteschlange. Wenn Sie eine Abfrage ausführen möchten, die lange Zeit in Anspruch nimmt, können Sie die Warteschlange in die Warteschlange stellen. Anschließend wird sie von einem der Threads aufgenommen und verarbeitet. Sie werden nie zu viele Threads haben, da die Anzahl Ihrer Threads begrenzt ist. 

Edit: Oder noch besser, nur einige Threads. Wenn ein Thread etwas in einer Warteschlange sieht, fragt er nach einer Verbindung aus dem Pool und verarbeitet sie. 

1
Amir Raminfar

Eine einfache Lösung besteht darin, Ihre jdbs-Aufrufe in CompletableFuture einzuhüllen und einen benutzerdefinierten Thread-Pool für diese Aufrufe bereitzustellen

0
oleffir