Ist ein asynchroner JDBC-Aufruf möglich?


158

Ich frage mich, ob es eine Möglichkeit gibt, asynchrone Aufrufe an eine Datenbank zu tätigen.

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 (indem Sie einen Listener / Rückruf oder etwas anderes übergeben). Ich möchte das Warten auf die Antwort der Datenbank nicht blockieren.

Ich halte die Verwendung eines Thread-Pools nicht für eine Lösung, da diese nicht skaliert werden kann. Bei starken gleichzeitigen Anforderungen wird eine sehr große Anzahl von Threads erzeugt.

Wir sind mit solchen Problemen bei Netzwerkservern konfrontiert und haben Lösungen gefunden, indem wir den Systemaufruf select / poll / epoll verwenden, um zu vermeiden, dass ein Thread pro Verbindung vorhanden ist. Ich frage mich nur, wie ich eine ähnliche Funktion mit Datenbankanforderung haben kann.

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

** Update **
Aufgrund des Mangels an echten praktischen Lösungen habe ich beschlossen, selbst eine Bibliothek (Teil von finagle) zu erstellen: finagle-mysql . Grundsätzlich dekodiert / decodiert es MySQL-Anfragen / Antworten und verwendet Finagle / Netty unter der Haube. Es lässt sich auch bei einer großen Anzahl von Verbindungen sehr gut skalieren.




Das Problem ist, wie die Datenbank den Client benachrichtigen kann, wenn die Abfrage abgeschlossen ist. Eine Möglichkeit wäre (z. B.), dass Oracle die Funktion "Benachrichtigung über Änderungsergebnisse von Datenbankabfragen" verwendet und benachrichtigt wird, wenn sich Datenbankdaten ändern. Dies gilt für SQL-Abfragen, die die Datenbankdaten ändern. Bei schreibgeschützten Abfragen würde dies nicht funktionieren. Andererseits bin ich mir nicht sicher, ob es eine gute Idee wäre, asynchrone Verbindungen herzustellen, da das Herstellen dieser Verbindungen teuer ist. Dies ist natürlich keine sehr allgemeine Lösung. Nur Denkanstöße ...
Mike Argyriou

Verwendet finagle-mysql JDBC?
Saeed Zarinfam

Antworten:


164

Ich verstehe nicht, wie einer der vorgeschlagenen Ansätze, die JDBC-Aufrufe in Akteure, Ausführende oder irgendetwas anderes einschließen, hier helfen kann - kann jemand klarstellen.

Das Grundproblem ist sicherlich, dass der JDBC-Operationsblock auf Socket-E / A blockiert. Wenn es dies tut, blockiert es den Thread, der am Ende der Geschichte läuft. Unabhängig davon, für welches Wrapping-Framework Sie sich entscheiden, wird ein Thread pro gleichzeitiger Anforderung beschäftigt / blockiert.

Wenn die zugrunde liegenden Datenbanktreiber (MySql?) Eine Möglichkeit bieten, die Socket-Erstellung abzufangen (siehe SocketFactory), kann ich mir vorstellen, dass eine asynchrone ereignisgesteuerte Datenbankebene auf der JDBC-API erstellt werden kann, die wir jedoch kapseln müssen ganze JDBC hinter einer ereignisgesteuerten Fassade, und diese Fassade würde nicht wie JDBC aussehen (nachdem sie ereignisgesteuert wäre). Die Datenbankverarbeitung würde in einem anderen Thread als dem Aufrufer asynchron erfolgen, und Sie müssten herausfinden, wie Sie einen Transaktionsmanager erstellen, der nicht auf der Thread-Affinität beruht.

So etwas wie der Ansatz, den ich erwähne, würde es sogar einem einzelnen Hintergrund-Thread ermöglichen, eine Ladung gleichzeitiger JDBC-Execs zu verarbeiten. In der Praxis würden Sie wahrscheinlich einen Thread-Pool 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-E / A ohne den Benutzer eines Auswahlmusters möglich ist - einfacher, nur die typische JDBC-Parallelität zu berechnen und zu setzen in einem Verbindungspool der richtigen Größe).


Es sieht so aus, als ob MySql wahrscheinlich etwas in der von mir vorgeschlagenen Richtung tut --- http://code.google.com/p/async-mysql-connector/wiki/UsageExample


1
Durch die Verwendung von Akka werden relationale DBs nicht asynchron aufgerufen. Sie können sie auf einer Reihe von dedizierten Threads für den DB-Zugriff ausführen. Auf diese Weise wird nicht die gesamte Site heruntergefahren, wenn die Site nicht mehr reagiert, da Sie in der Service-Schicht immer asynchrone Aufrufe an die DAO-Schicht mit Versprechungen getätigt haben und Ihre Webserver-Threads vom Rest Ihrer Anwendung getrennt sind.
Onur

Akteure sind nicht die einzigen Problemumgehungen (z. B. Mikrodienste und asynchrone http, die wir auf Tausende pro Sekunde skalieren), und ich würde sie aus Sicht des Kunden nicht so schnell als nicht asynchron abtun. Wenn 1k UI-Threads in Ihr System gelangen und nur 10 Threads in der Datenbank blockiert sind, werden 990 'Nachrichten' (oder ähnliches) im Speicher in die Warteschlange gestellt, ohne einen der 1k UI-Threads zu blockieren (die wahrscheinlich freigegeben werden). .. ist das nicht erforderlich? Ich würde gerne echte asynchrone JDBC sehen, aber das bedeutet nicht, dass es in der Zwischenzeit keine äußerst praktikablen Problemumgehungen gibt.
Greg Pendlebury

42

Es ist unmöglich, einen asynchronen Aufruf der Datenbank über JDBC zu tätigen , aber Sie können mit Actors asynchrone Aufrufe an JDBC mit Actors tätigen (z. B. ruft der Actor über DBBC die DB an und sendet Nachrichten an Dritte, wenn die Anrufe beendet sind). oder, wenn Sie CPS mögen, mit Pipeline-Futures (Versprechungen) (eine gute Implementierung ist Scalaz Promises )

Ich halte die Verwendung eines Thread-Pools nicht für eine Lösung, da diese nicht skaliert werden kann. Bei starken gleichzeitigen Anforderungen wird eine sehr große Anzahl von Threads erzeugt.

Scala-Akteure sind standardmäßig ereignisbasiert (nicht threadbasiert). Durch die Fortführungsplanung können Millionen von Akteuren in einem Standard-JVM-Setup erstellt werden.

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 macht die Synchronität von JDBC für mich vollkommen Sinn. Die Kosten für eine Datenbanksitzung sind weitaus höher als die Kosten für das Blockieren des Java-Threads (entweder im Vordergrund oder im Hintergrund) und das Warten auf eine Antwort. Wenn Ihre Abfragen so lange ausgeführt werden, dass die Funktionen eines Executor-Dienstes (oder das Umschließen von Actor / Fork-Join / Promise-Parallelitäts-Frameworks) nicht ausreichen (und Sie zu viele Threads verbrauchen), sollten Sie zunächst über Ihre nachdenken Datenbank laden. Normalerweise kommt die Antwort von einer Datenbank sehr schnell zurück, und ein Executor-Service, der mit einem festen Thread-Pool unterstützt wird, ist eine ausreichend gute Lösung. Wenn Sie zu viele lang laufende Abfragen haben, sollten Sie eine Vorverarbeitung (Vor-) Verarbeitung in Betracht ziehen - wie eine nächtliche Neuberechnung der Daten oder ähnliches.


2
@ Victor, jeder Schauspieler, der parallel an einer Blockierungsoperation (JDBC) arbeitet, wird in einem separaten Thread ausgeführt, den Steve zu vermeiden versucht
Vasil Remeniuk

36
Der Actor-Ansatz erfordert immer noch einen Thread pro aktiver Datenbanktransaktion, während die Transaktion ausgeführt wird. Dies ist also keine wirkliche Lösung für das OP-Problem, es sei denn, Sie sind bereit, die Anzahl paralleler Datenbanktransaktionen zu beschränken und einige "asynchrone" Datenbankoperationen warten zu lassen für einige bereits ausgeführte, um einen Thread zu beenden und freizugeben. Dies ist jedoch keine schlechte Idee - die Datenbank kann überlastet werden, wenn Sie zu viele Verbindungen öffnen. Daher ist es hilfreich, Ihre Datenbanktransaktion zur Verarbeitung in eine Warteschlange zu stellen, anstatt den Verarbeitungsthread für http-Anforderungen zu blockieren.
Dobes Vandermeer

8
Eine akteurbasierte Lösung blockiert immer noch den Thread. Sagen Sie nicht, dass es nicht möglich ist, einen asynchronen JDBC-Aufruf auszuführen. Es gibt experimentelle Open-Source-Bibliotheken, die versuchen, einen asynchronen JDBC zu implementieren.

6
+1 "Die Kosten einer Datenbanksitzung sind weitaus höher als die Kosten für das Blockieren des Java-Threads"
Paul Draper

1
Bei teuren DB-Aufrufen gibt es normalerweise kein so großes Problem. Wenn der Anruf trivial ist, wird der Netzwerk-Overhead zu einem Problem. Wenn Sie 100 Abfragen durchführen möchten, die jeweils 1 ms in der Datenbank dauern, der Netzwerk-Overhead jedoch 200 ms beträgt, dauert dies synchron über 20 Sekunden, dauert jedoch asynchron 300 ms.
Morten

12

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

  • Senden Sie eine Nachricht an eine Warteschlange, in der die Abonnenten die Nachricht akzeptieren, und führen Sie den SQL-Prozess aus. Ihr Hauptprozess wird weiterhin ausgeführt und akzeptiert oder sendet neue Anforderungen.

  • Wenn der SQL-Prozess beendet ist, können Sie wie folgt vorgehen: Senden Sie eine Nachricht mit dem Ergebnis des Prozesses an eine ResponseQueue, und ein Listener auf der Clientseite akzeptiert sie und führt den Rückrufcode aus.


7

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

"Ich halte die Verwendung eines Thread-Pools nicht für eine Lösung, da diese nicht skaliert werden kann. Bei starken gleichzeitigen Anforderungen wird eine sehr große Anzahl von Threads erzeugt."

Ich bin gespannt, warum ein begrenzter Pool von Threads nicht skaliert. Es ist ein Pool, der kein Thread pro Anforderung ist, um einen Thread pro Anforderung zu erzeugen. Ich benutze dies seit einiger Zeit auf einer Webapp mit hoher Last und wir haben bisher keine Probleme gesehen.


Ich denke, dass das Hauptargument gegen Threads darin besteht, dass Sie sich im Grunde genommen außerhalb der Standardbeschränkungen für Java-Container befinden, sodass Sie das Container-verwaltete Clustering verlieren und Failover-Funktionen ausführen, obwohl Sie Ihre eigenen rollen oder etwas wie Terrakotta verwenden könnten.
Mezmo

3
Mithilfe von Arbeitsmanagern können wir von App Servern verwaltete Thread-Umfragen abrufen. Websphere, Weblogic und Glassfish unterstützen es
Aravind Yarram


4

Wie in anderen Antworten erwähnt, ist die JDBC-API von Natur aus nicht asynchron.
Wenn Sie jedoch mit einer Teilmenge der Vorgänge 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.


3

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

Derzeit gibt es 2 experimentelle nativ asynchrone Treiber für MySQL und Postgresql.


Ich möchte diesen Ansatz bereithalten. JDBC hat sich von Anfang an stark weiterentwickelt (Iteratoren, Vorlagen, vorbereitete Prozeduren), aber dieser asynchrone Ansatz wurde nie implementiert. Es wäre besonders interessant für Schreibvorgänge (Einfügen, Aktualisieren, Löschen) und insbesondere für solche schweren Batch-Sendungen, mit denen wir alle konfrontiert sind. Meiner Meinung nach würde jede Art von Client-basiertem Ansatz (Pooling, Actor, Scheduling, Messaging ...) zu geringen Belohnungen in Bezug auf die Ressourcennutzung führen (wahrscheinlich einige Gewinne beim Durchsatz oder bei der Latenz).
Jaime Casero

Alt und verlassen, nur zwei Datentypen unterstützt und noch nicht einmal produktionsbereit. Leider :(
Aaron Zinman

In Ausgabe 1 dieser Bibliothek geht es darum, dass die Website nicht verfügbar ist . Es ist mehr als ein Jahr alt. Ich vermute, diese Bibliothek ist ziemlich tot.
Lukas Eder

3

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 Anbieter stellt eine Erweiterung für JDBC und einen Wrapper zur Verfügung, mit dem JDBC verarbeitet werden kann. Es ist jedoch möglich, JDBC selbst mit einer Verarbeitungswarteschlange zu versehen und eine Logik zu implementieren, die die Warteschlange auf einer oder mehreren separaten Verbindungen verarbeiten kann. Ein Vorteil für einige Arten von Aufrufen besteht darin, dass die Logik bei ausreichender Last die Aufrufe zur Verarbeitung in JDBC-Stapel konvertieren 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 hierfür ist, wenn Einfügungen durchgeführt werden, um Benutzeraktivitäten zu protokollieren. Die Bewerbung hat gewonnen '

Nebenbei bemerkt, ein Produkt auf dem Markt bietet einen richtliniengesteuerten Ansatz, um asynchrone Anrufe wie die von mir beschriebenen asynchron zu ermöglichen ( http://www.heimdalldata.com/ ). Haftungsausschluss: Ich bin Mitbegründer dieser Firma. Es ermöglicht die Anwendung regulärer Ausdrücke auf Datentransformationsanforderungen wie Einfügen / Aktualisieren / Löschen für jede JDBC-Datenquelle und stapelt diese automatisch zur Verarbeitung. Bei Verwendung mit MySQL und der Option rewriteBatchedStatements ( MySQL und JDBC mit rewriteBatchedStatements = true ) kann dies die Gesamtlast der Datenbank erheblich verringern .


Dies bedeutet jedoch weiterhin, dass JDBC mindestens einen separaten Thread haben sollte. Was ist mit Frameworks und Stacks, die Single-Threaded sind, aber dennoch auf Rückrufen basieren (man denke an nodejs)? Wissen Sie, wie sie JDBC-Anrufe verwalten?
Yuranos

3

Sie haben meiner Meinung nach drei Möglichkeiten:

  1. Verwenden Sie eine gleichzeitige Warteschlange , 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 einem anderen Knoten (dh einem anderen Prozess oder Computer) durch und lassen Sie Ihren Datenbankclient asynchrone Netzwerkaufrufe an diesen Knoten ausführen.
  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.


3

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

Personen, die skalieren und gleichzeitig die Verwendung relationaler Datenbanken beibehalten möchten, sind aufgrund bestehender Standards, die auf dem Blockieren 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 arbeitet.

R2DBC ist eine Spezifikation, die von Grund auf für die reaktive Programmierung mit SQL-Datenbanken entwickelt wurde und eine nicht blockierende SPI für Datenbanktreiberimplementierer und Clientbibliotheksautoren definiert. R2DBC-Treiber implementieren das Datenbank-Wire-Protokoll vollständig auf einer nicht blockierenden E / A-Schicht.

R2DBCs Website

GitHub von R2DBC

Funktionsmatrix

Geben Sie hier die Bildbeschreibung ein


2

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

Sie können eine feste Anzahl von Threads haben, um lang laufende Vorgänge abzuwickeln. Und stattdessen Runnablekönnen Sie verwenden Callable, die ein Ergebnis zurückgeben. Das Ergebnis ist in einem Future<ReturnType>Objekt gekapselt , sodass Sie es abrufen können, wenn es zurück ist.



2

Nur eine verrückte Idee: Sie könnten ein Iteratee-Muster über JBDC resultSet verwenden, das in eine Zukunft / ein Versprechen eingewickelt ist

Hammersmith macht das für MongoDB .


1

Ich denke hier nur an Ideen. Warum konnten Sie keinen Pool von Datenbankverbindungen haben, von denen jede einen Thread hat? Jeder Thread hat Zugriff auf eine Warteschlange. Wenn Sie eine Abfrage ausführen möchten, die lange dauert, können Sie sie in die Warteschlange stellen. Einer der Threads nimmt sie auf und verarbeitet sie. Sie werden nie zu viele Threads haben, da die Anzahl Ihrer Threads begrenzt ist.

Edit: Oder noch besser, nur eine Reihe von Threads. Wenn ein Thread etwas in einer Warteschlange sieht, fragt er nach einer Verbindung aus dem Pool und behandelt diese.


1

Die Bibliothek commons-dbutils unterstützt eine, für AsyncQueryRunnerdie Sie eine bereitstellen ExecutorService, und gibt eine zurück Future. Es lohnt sich, einen Blick darauf zu werfen, da es einfach zu bedienen ist und sicherstellt, dass keine Ressourcen verloren gehen.


1

Wenn Sie an asynchronen Datenbank-APIs für Java interessiert sind, 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 der README von erwähnt das Github-Projekt.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.