web-dev-qa-db-fra.com

L'appel jdbc asynchrone est-il possible?

Je me demande s'il existe un moyen de faire des appels asynchrones à une base de données?

Par exemple, imaginons que j'ai une grosse requête dont le traitement prend beaucoup de temps, je veux envoyer la requête et recevoir une notification quand la requête retournera une valeur (en passant un Listener/callback ou quelque chose du genre). Je ne veux pas bloquer l'attente de la base de données pour répondre.

Je ne considère pas que l'utilisation d'un pool de threads soit une solution, car elle ne s'adapte pas à l'échelle. Dans le cas de demandes simultanées lourdes, cela engendrera un très grand nombre de threads.

Nous sommes confrontés à ce type de problème avec les serveurs de réseau et nous avons trouvé des solutions en utilisant l'appel système select/poll/epoll afin d'éviter d'avoir un thread par connexion. Je me demande simplement comment avoir une fonctionnalité similaire avec une demande de base de données?

Remarque: Je suis conscient que l'utilisation de FixedThreadPool peut être une bonne solution, mais je suis surpris que personne n'ait développé un système vraiment asynchrone (sans utiliser de thread supplémentaire).

** Mettre à jour **
En raison de l’absence de solutions pratiques concrètes, j’ai décidé de créer moi-même une bibliothèque (qui fait partie de finagle): finagle-mysql . Il décode/décode les requêtes/réponses mysql et utilise Finagle/Netty sous le capot. Il évolue extrêmement bien même avec un très grand nombre de connexions.

141
Steve Gury

Je ne comprends pas comment les approches proposées qui englobent les appels de JDBC dans des acteurs, des exécuteurs ou toute autre chose peuvent aider ici - quelqu'un peut-il clarifier.

Le problème fondamental est certainement que les opérations JDBC bloquent sur le socket IO. Quand cela se produit, il bloque le fil de discussion en cours - fin de l'histoire. Quel que soit le framework d’enveloppement que vous choisissez d’utiliser, il finira par garder un thread occupé/bloqué par requête simultanée.

Si les pilotes de base de données sous-jacents (MySql?) Offrent un moyen d'intercepter la création du socket (voir SocketFactory), alors j'imagine qu'il serait possible de créer une couche de base de données gérée par des événements asynchrones au-dessus de l'API JDBC, mais nous devrions encapsuler les JDBC entière derrière une façade événementielle, et cette façade ne ressemblerait pas à JDBC (après avoir été événementielle). Le traitement de la base de données se déroulerait de manière asynchrone sur un thread différent de l'appelant, et vous auriez à déterminer comment créer un gestionnaire de transactions qui ne repose pas sur une affinité de thread.

Une approche similaire à celle que je viens de mentionner permettrait même à un seul fil d’arrière-plan de traiter une charge d’exécuteurs JDBC simultanés. En pratique, vous exécuterez probablement un pool de threads pour utiliser plusieurs cœurs.

(Bien entendu, je ne commente pas la logique de la question initiale, mais uniquement les réponses qui impliquent que la simultanéité dans un scénario avec socket bloquant IO est possible sans l'utilisateur d'un modèle de sélecteur - il est plus simple de calculer votre Accès simultané JDBC et mettre dans un pool de connexion de la bonne taille).


On dirait que MySql fait probablement quelque chose dans le sens que je suggère --- http://code.google.com/p/async-mysql-connector/wiki/UsageExample

147
johnlon

Il est impossible de passer un appel asynchrone à la base de données via JDBC, mais vous pouvez effectuer des appels asynchrones vers JDBC avec Actors (par exemple, un acteur appelle la base de données via JDBC. , et envoie des messages aux tiers, lorsque les appels sont terminés), ou, si vous aimez CPS, avec pipelined futures (promises) (une bonne implémentation est ScalazPromises )

Je ne considère pas que l'utilisation d'un pool de threads soit une solution, car elle ne s'adapte pas à l'échelle. Dans le cas de demandes simultanées lourdes, cela engendrera un très grand nombre de threads.

Les acteurs Scala par défaut sont basés sur des événements (et non sur des threads) - la planification de la continuation permet de créer des millions d’acteurs sur une configuration JVM standard. 

Si vous ciblez Java, Akka Framework est une implémentation du modèle Actor qui possède une bonne API pour Java et Scala.


À part cela, la nature synchrone de JDBC me semble parfaitement logique. Le coût d'une session de base de données est beaucoup plus élevé que le coût du thread Java bloqué (en avant ou en arrière-plan) et dans l'attente d'une réponse. Si vos requêtes durent depuis si longtemps que les fonctionnalités d'un service exécuteur (ou l'encapsulation de cadres de concurrence Acteur/fork-join/promise) ne vous suffisent pas (et que vous utilisez trop de threads), vous devez tout d'abord penser à votre chargement de la base de données. Normalement, la réponse d’une base de données revient très vite et un service d’exécuteur supporté avec un pool de threads fixe est une bonne solution. Si vous avez trop de requêtes de longue durée, vous devez envisager un traitement (préalable) préalable, comme un recalcul nocturne des données ou quelque chose du genre.

40
Vasil Remeniuk

Vous pourriez peut-être utiliser un système de messagerie asynchrone JMS, qui évolue plutôt bien, à mon humble avis:

  • Envoyez un message à une file d'attente où les abonnés accepteront le message et exécuteront le processus SQL. Votre processus principal continuera à s'exécuter et accepter ou envoyer de nouvelles demandes.

  • Lorsque le processus SQL se termine, vous pouvez exécuter le processus opposé: envoyez un message à ResponseQueue avec le résultat du processus et un écouteur du côté client l'accepte et exécute le code de rappel.

11
Tomas Narros

JDBC n’est pas directement pris en charge, mais vous disposez de plusieurs options telles que MDB, Executors from Java 5.

"Je ne considère pas que l’utilisation d’un pool de threads soit une solution car elle n’évolue pas. Dans le cas de demandes simultanées lourdes, cela engendrera un très grand nombre de threads."

Je suis curieux de savoir pourquoi un groupe de threads délimité ne serait pas mis à l'échelle. Il s'agit d'un pool pas thread-per-request pour générer un thread pour chaque demande. Je l'utilise depuis assez longtemps sur une application Web chargée et nous n'avons encore rencontré aucun problème.

7

Il semble qu'une nouvelle API asynchrone jdbc, "JDBC next", est en préparation.

Voir présentation ici

Vous pouvez télécharger l’API de ici

4
Sebastien

Comme mentionné dans d'autres réponses, l'API JDBC n'est pas asynchrone par nature.
Cependant, si vous pouvez vivre avec un sous-ensemble d'opérations et une API différente, il existe des solutions. Un exemple est https://github.com/jasync-sql/jasync-sql qui fonctionne pour MySQL et PostgreSQL.

4
oshai

Une solution est en cours de développement pour rendre la connectivité réactive possible avec des bases de données relationnelles standard.

Les personnes qui souhaitent évoluer tout en conservant l'utilisation des bases de données relationnelles sont coupées de la programmation réactive en raison des normes existantes basées sur le blocage des E/S. R2DBC spécifie une nouvelle API permettant un code réactif fonctionnant efficacement avec des bases de données relationnelles.

R2DBC est une spécification conçue dès le départ pour la programmation réactive avec des bases de données SQL définissant un SPI non bloquant pour les développeurs de pilotes de base de données et les auteurs de bibliothèques clientes. Les pilotes R2DBC implémentent pleinement le protocole de connexion à la base de données au-dessus d'une couche d'E/S non bloquante.

Site Web de R2DBC

GitHub de R2DBC

Feature Matrix

enter image description here

3
Yassin Hajaj

Une vieille question, mais quelques informations supplémentaires. JDBC ne peut pas émettre de requêtes asynchrones à la base de données elle-même, à moins qu'un fournisseur ne fournisse une extension à JDBC et un wrapper permettant de gérer JDBC. Cela dit, il est possible d’envelopper JDBC lui-même avec une file d’attente de traitement et d’implémenter une logique capable de traiter celle-ci en dehors d’une ou plusieurs connexions distinctes. Un avantage de ceci pour certains types d’appels est que la logique, si elle est soumise à une charge suffisante, peut convertir les appels en lots JDBC pour traitement, ce qui peut considérablement accélérer la logique. Ceci est particulièrement utile pour les appels dans lesquels des données sont insérées et le résultat réel doit uniquement être enregistré en cas d'erreur. Un bon exemple de cela est si des insertions sont effectuées pour enregistrer l'activité des utilisateurs. L'application ne se souciera pas si l'appel se termine immédiatement ou dans quelques secondes.

En guise de remarque, un produit sur le marché offre une approche basée sur des règles permettant aux appels asynchrones tels que ceux décrits précédemment d'être passés de manière asynchrone ( http://www.heimdalldata.com/ ). Disclaimer: Je suis co-fondateur de cette société. Il permet d'appliquer des expressions régulières aux demandes de transformation de données, telles que des insertions/mises à jour/suppressions, pour toute source de données JDBC, et les met automatiquement en lot pour le traitement. Utilisé avec MySQL et l'option rewriteBatchedStatements ( MySQL et JDBC avec rewriteBatchedStatements = true ), cela peut considérablement réduire la charge globale de la base de données.

3
Erik Brandsberg

Les exécuteurs Java 5.0 pourraient être utiles.

Vous pouvez avoir un nombre fixe de threads pour gérer les opérations de longue durée. Et au lieu de Runnable, vous pouvez utiliser Callable, qui renvoie un résultat. Le résultat est encapsulé dans un objet Future<ReturnType> , afin que vous puissiez l’obtenir à son retour.

3
Bozho

Le projet Ajdbc semble répondre à ce problème http://code.google.com/p/adbcj/

Il existe actuellement 2 pilotes expérimentaux nativement asynchrones pour mysql et postgresql.

3
Sebastien

Vous avez trois options à mon avis:

  1. Utilisez une file d'attente simultanée pour distribuer les messages sur un nombre de threads faible et fixe. Donc, si vous avez 1000 connexions, vous aurez 4 threads, pas 1000 threads.
  2. Accédez à la base de données sur un autre noeud (c’est-à-dire un autre processus ou une machine) et demandez à votre client de base de données de passer appels réseau asynchrones à ce noeud.
  3. Implémentez un véritable système distribué via des messages asynchrones. Pour cela, vous aurez besoin d'une file d'attente de messagerie telle que CoralMQ ou Tibco.

Diclaimer: Je suis l'un des développeurs de CoralMQ.

2
rdalmeida

Juste une idée folle: vous pouvez utiliser un motif Iteratee par-dessus JBDC resultSet enveloppé dans quelque avenir/promesse

Hammersmith fait cela pour MongoDB .

2
jwinandy

La bibliothèque commons-dbutils prend en charge une AsyncQueryRunner à laquelle vous fournissez une ExecutorService et elle retourne une Future. Cela vaut la peine d’être vérifié, car il est simple à utiliser et évite les fuites de ressources.

1
William Speirs

Si vous êtes intéressé par les API de base de données asynchrones pour Java, sachez qu'il existe une nouvelle initiative pour créer un ensemble d'API standard basées sur CompletableFuture et lambdas. Il existe également une implémentation de ces API sur JDBC, qui peut être utilisée pour les utiliser: https://github.com/Oracle/oracle-db-examples/tree/master/Java/AoJ Le JavaDoc est mentionné dans le README du projet github.

1
Jean de Lavarene

Je ne fais que penser à des idées ici. Pourquoi ne pourriez-vous pas avoir un pool de connexions de base de données, chacune ayant un thread. Chaque thread a accès à une file d'attente. Lorsque vous souhaitez effectuer une requête qui prend beaucoup de temps, vous pouvez mettre en file d'attente, puis l'un des threads la prendra et la gérera. Vous n'aurez jamais trop de threads car le nombre de vos threads est limité. 

Edit: Ou mieux encore, juste un certain nombre de threads. Lorsqu'un thread voit quelque chose dans une file d'attente, il demande une connexion au pool et la gère. 

1
Amir Raminfar

Une solution simple consiste à encapsuler vos appels jdbs dans CompletableFuture et à fournir un pool de threads personnalisé pour ces appels.

0
oleffir