Un appel jdbc asynchrone est-il possible?


158

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

Par exemple, imaginez que j'ai une grosse demande qui prend très longtemps à traiter, je veux envoyer la demande et recevoir une notification lorsque la demande renverra une valeur (en passant un écouteur / rappel ou quelque chose). Je ne veux pas bloquer l'attente de la réponse de la base de données.

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

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

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

** Mise à jour **
Faute de vraies solutions pratiques, j'ai décidé de créer moi - même une librairie (partie de finagle): finagle-mysql . Il décode / décode la requête / réponse mysql et utilise Finagle / Netty sous le capot. Il évolue extrêmement bien même avec un grand nombre de connexions.




Le problème est de savoir comment la base de données pourrait avertir le client lorsque la requête se termine. L'une d'entre elles serait (par exemple) qu'Oracle utilise la fonction "Notification de changement de résultat de requête de base de données" et soit averti lorsque les données de base de données changent. Cela s'applique aux requêtes SQL qui modifient les données de la base de données. Pour les requêtes en lecture seule, cela ne fonctionnerait pas. D'un autre côté, je ne suis pas sûr que l'établissement de connexions asynchrones soit une bonne idée car les établir coûte cher. Bien entendu, ce n'est pas une solution très générale. Juste matière à réflexion ...
Mike Argyriou

Est-ce que finagle-mysql utilise JDBC?
Saeed Zarinfam

Réponses:


164

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

Le problème fondamental est certainement que les opérations JDBC se bloquent sur le socket IO. Quand il fait cela, il bloque le fil de discussion à la fin de l'histoire. Quel que soit le cadre d'encapsulation que vous choisissez d'utiliser, un thread sera maintenu 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 de socket (voir SocketFactory), alors j'imagine qu'il serait possible de créer une couche de base de données asynchrone pilotée par les événements au-dessus de l'API JDBC, mais nous devrons encapsuler le tout JDBC derrière une façade événementielle, et cette façade ne ressemblerait pas à JDBC (après elle serait événementielle). Le traitement de la base de données se produirait de manière asynchrone sur un thread différent de celui de l'appelant, et vous devrez déterminer comment créer un gestionnaire de transactions qui ne repose pas sur l'affinité des threads.

Quelque chose comme l'approche que je mentionne permettrait même à un seul thread d'arrière-plan de traiter une charge d'exécutions JDBC simultanées. En pratique, vous exécuteriez probablement un pool de threads pour utiliser plusieurs cœurs.

(Bien sûr, je ne fais pas de commentaires sur la logique de la question d'origine, mais uniquement les réponses qui impliquent que la concurrence dans un scénario avec blocage d'E / S de socket est possible sans l'utilisateur d'un modèle de sélecteur - plus simple simplement pour déterminer votre concurrence JDBC typique et mettre dans un pool de connexions de la bonne taille).


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


1
L'utilisation d'Akka ne fait pas d'appels aux bases de données relationnelles asynchrones. Il vous permet de les exécuter sur un tas de threads dédiés pour accéder facilement à la base de données. De cette façon, vous ne supprimez pas tout le site lorsque le site ne répond plus car vous avez toujours effectué des appels asynchrones dans la couche de service vers la couche DAO avec des promesses et vos threads de serveur Web sont séparés du reste de votre application.
Onur

Les acteurs ne sont pas les seules solutions de contournement (par exemple, les micro-services et asynchrone http, que nous adaptons à des milliers par seconde), et je ne serais pas si prompt à les rejeter comme étant non asynchrones du point de vue du client. Si le trafic de 1k threads de l'interface utilisateur entre dans votre système et que seulement 10 threads sont bloqués sur la base de données, alors que 990 `` messages '' (ou quelque chose de similaire) sont mis en file d'attente en mémoire sans bloquer aucun des threads de 1k UI (qui seront probablement libérés). .. n'est-ce pas ce qui est requis? J'adorerais voir un vrai JDBC asynchrone, mais cela ne signifie pas qu'il n'y a pas de solutions de contournement extrêmement viables dans l'intervalle.
Greg Pendlebury

42

Il est impossible de faire un appel asynchrone à la base de données via JDBC, mais vous pouvez faire des appels asynchrones à JDBC avec des acteurs (par exemple, l'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 des futures en pipeline (promesses) (une bonne implémentation est Scalaz Promises )

Je ne considère pas que l'utilisation d'un pool de threads soit une solution car elle ne s'adapte pas, 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 les événements (et non sur les threads) - la planification de continuation permet de créer des millions d'acteurs sur une configuration JVM standard.

Si vous ciblez Java, Akka Framework est une implémentation de modèle Actor qui dispose d'une bonne API à la fois pour Java et Scala.


En dehors de cela, la nature synchrone de JDBC me semble parfaitement logique. Le coût d'une session de base de données est bien plus élevé que le coût du thread Java bloqué (en avant ou en arrière-plan) et en attente d'une réponse. Si vos requêtes durent si longtemps que les capacités d'un service d'exécuteur (ou d'encapsulation des frameworks de concurrence Actor / fork-join / promise) ne vous suffisent pas (et que vous consommez trop de threads), vous devez d'abord penser à votre chargement de la base de données. Normalement, la réponse d'une base de données revient très rapidement, et un service exécuteur soutenu avec un pool de threads fixe est une solution suffisante. Si vous avez trop de requêtes de longue durée, vous devriez envisager un (pré-) traitement initial - comme un recalcul nocturne des données ou quelque chose comme ça.


2
@Victor, chaque acteur travaillant en parallèle sur une opération de blocage (JDBC) s'exécutera sur un thread séparé que Steve essaie d'éviter
Vasil Remeniuk

36
L'approche acteur nécessite toujours un thread par transaction de base de données active, pendant que la transaction est en cours, donc ce n'est pas vraiment une solution au problème de l'OP à moins que vous ne souhaitiez limiter le nombre de transactions de base de données parallèles et faire attendre certaines opérations de base de données «asynchrones» pour certains déjà en cours d'exécution pour terminer et libérer un fil. Ce n'est pas une mauvaise idée, cependant - la base de données peut être surchargée si vous ouvrez trop de connexions - donc mettre votre transaction de base de données dans une file d'attente pour traitement au lieu de bloquer votre thread de traitement de demande http vous aidera.
Dobes Vandermeer

8
La solution basée sur les acteurs bloque toujours le thread. Ne dites pas qu'il n'est pas possible d'exécuter un appel async jdbc, il existe des bibliothèques open source expérimentales qui essaient d'implémenter async jdbc.

6
+1 "Le coût d'une session de base de données est bien plus élevé que le coût du thread Java bloqué"
Paul Draper

1
Pour les appels DB coûteux, il n'y a généralement pas un si gros problème. C'est lorsque l'appel est trivial que la surcharge du réseau devient un problème. Si vous souhaitez effectuer 100 requêtes, qui prennent 1 ms chacune sur la base de données, mais que la surcharge du réseau est de 200 ms, cela prendra plus de 20 secondes de manière synchrone, mais prendrait 300 ms de manière asynchrone.
morten

12

Peut-être pourriez-vous utiliser un système de messagerie asynchrone JMS, qui évolue assez 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 de fonctionner et d'accepter ou d'envoyer de nouvelles demandes.

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


7

Il n'y a pas de support direct dans JDBC mais vous avez plusieurs options comme MDB, Executors from Java 5.

"Je ne considère pas que l'utilisation d'un pool de threads soit une solution car elle ne s'adapte pas, dans le cas de requêtes simultanées lourdes, cela engendrera un très grand nombre de threads."

Je suis curieux de savoir pourquoi un pool limité de threads ne va pas à l'échelle? Il s'agit d'un pool et non d'un thread par requête pour générer un thread pour chaque requête. J'utilise cela depuis un certain temps sur une application Web à forte charge et nous n'avons vu aucun problème jusqu'à présent.


Je pense que le principal argument contre les threads est que vous êtes essentiellement en dehors des contraintes de conteneurs Java standard, vous perdez donc les capacités de clustering et de basculement gérées par conteneur, même si vous pouvez utiliser les vôtres ou utiliser quelque chose comme Terracotta.
mezmo

3
nous pouvons exploiter les sondages de thread gérés par le serveur d'applications en utilisant des gestionnaires de travail. websphere, weblogic et glassfish le soutiennent
Aravind Yarram


4

Comme mentionné dans d'autres réponses, l'API JDBC n'est pas Async de par sa nature.
Cependant, si vous pouvez vivre avec un sous-ensemble des 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.


3

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.


J'aimerais que cette approche soit prête. JDBC a beaucoup évolué depuis le début (itérateurs, modèles, procédures préparées), mais cette approche asynchrone n'a jamais été implémentée. Ce serait particulièrement intéressant pour les opérations d'écriture (Insertion, Mise à jour, Suppression), et spécialement pour les TX par lots lourds auxquels nous sommes tous confrontés. À mon avis, tout type d'approche basée sur le client (pooling, acteur, planification, messagerie ...) entraînerait peu de récompenses en termes d'utilisation des ressources (probablement quelques gains de débit ou de latence).
Jaime Casero

Anciens et abandonnés, seuls deux types de données pris en charge et même pas prêts pour la production. Malheureusement :(
Aaron Zinman

Le numéro 1 de cette bibliothèque concerne le fait que le site Web n'est pas disponible . Il a plus d'un an. Je soupçonne que cette bibliothèque est assez morte.
Lukas Eder

3

Une vieille question, mais quelques informations supplémentaires. Il n'est pas possible que JDBC émette des requêtes asynchrones vers la base de données elle-même, à moins qu'un fournisseur ne fournisse une extension à JDBC et un wrapper pour gérer JDBC. Cela dit, il est possible d'encapsuler JDBC lui-même avec une file d'attente de traitement et d'implémenter une logique permettant de traiter hors de la file d'attente sur une ou plusieurs connexions séparées. Un avantage de ceci pour certains types d'appels est que la logique, si elle est soumise à une charge suffisamment élevée, pourrait convertir les appels en lots JDBC pour le traitement, ce qui peut accélérer considérablement la logique. Ceci est très utile pour les appels où des données sont insérées, et le résultat réel ne doit être consigné qu'en cas d'erreur. Un bon exemple de ceci est si des insertions sont effectuées pour enregistrer l'activité de l'utilisateur. L'application a gagné '

En remarque, un produit sur le marché offre une approche politique pour permettre aux appels asynchrones comme ceux que j'ai décrits d'être effectués de manière asynchrone ( http://www.heimdalldata.com/ ). Avertissement: 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 l'insertion / la mise à jour / la suppression de toute source de données JDBC, et les regroupera automatiquement pour le traitement. Lorsqu'il est utilisé avec MySQL et l'option rewriteBatchedStatements ( MySQL et JDBC avec rewriteBatchedStatements = true ), cela peut réduire considérablement la charge globale de la base de données.


Mais cela signifie toujours que JDBC doit avoir au moins un thread séparé. Qu'en est-il des frameworks et des piles qui sont à thread unique mais toujours basés sur le rappel (nodejs vient à l'esprit)? Savez-vous comment ils gèrent les appels JDBC?
yuranos le

3

Vous avez trois options à mon avis:

  1. Utilisez une file d' attente simultanée pour distribuer les messages sur un nombre restreint et fixe de threads. Donc, si vous avez 1000 connexions, vous aurez 4 threads, pas 1000 threads.
  2. Accédez à la base de données sur un autre nœud (c'est-à-dire un autre processus ou une autre machine) et demandez à votre client de base de données d'effectuer des appels réseau asynchrones vers ce nœud.
  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.


3

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

Les personnes souhaitant é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 qui permet un code réactif qui fonctionne efficacement avec les 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 implémenteurs de pilotes de base de données et les auteurs de bibliothèques clientes. Les pilotes R2DBC implémentent entièrement le protocole filaire de base de données au-dessus d'une couche d'E / S non bloquante.

Site Web de R2DBC

GitHub de R2DBC

Matrice des fonctionnalités

entrez la description de l'image ici


2

Les exécuteurs Java 5.0 peuvent être utiles.

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



2

Juste une idée folle: vous pouvez utiliser un modèle Iteratee sur JBDC resultSet enveloppé dans un Future / Promise

Hammersmith fait cela pour MongoDB .


1

Je pense juste à des idées ici. Pourquoi ne pouviez-vous pas avoir un pool de connexions de base de données avec chacune 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 la mettre dans la file d'attente, puis l'un des threads la récupérera et la traitera. Vous n'aurez jamais trop de threads car le nombre de vos threads est limité.

Edit: Ou mieux encore, juste un certain nombre de fils. Lorsqu'un thread voit quelque chose dans une file d'attente, il demande une connexion à partir du pool et le gère.


1

La bibliothèque commons-dbutils prend en charge un pour AsyncQueryRunnerlequel vous fournissez un ExecutorServiceto et renvoie un fichier Future. Vaut le détour car il est simple à utiliser et vous assure de ne pas perdre de ressources.


1

Si vous êtes intéressé par les API de base de données asynchrones pour Java, sachez qu'il existe une nouvelle initiative visant à proposer 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 pratiquer ces API: https://github.com/oracle/oracle-db-examples/tree/master/java/AoJ Le JavaDoc est mentionné dans le README de le projet github.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.