È possibile la chiamata asincrona jdbc?


158

Mi chiedo se esiste un modo per effettuare chiamate asincrone a un database?

Ad esempio, immagina di avere una grande richiesta che impiega molto tempo per l'elaborazione, voglio inviare la richiesta e ricevere una notifica quando la richiesta restituirà un valore (passando un Listener / callback o qualcosa del genere). Non voglio bloccare in attesa che il database risponda.

Non ritengo che l'uso di un pool di thread sia una soluzione perché non si adatta, nel caso di richieste simultanee pesanti ciò genererà un numero molto elevato di thread.

Stiamo affrontando questo tipo di problema con i server di rete e abbiamo trovato soluzioni usando la chiamata di sistema select / poll / epoll per evitare di avere un thread per connessione. Mi sto solo chiedendo come avere una funzione simile con la richiesta del database?

Nota: sono consapevole del fatto che l'utilizzo di FixedThreadPool può essere una buona soluzione, ma sono sorpreso che nessuno abbia sviluppato un sistema veramente asincrono (senza l'utilizzo di thread aggiuntivi).

** Aggiornamento **
A causa della mancanza di soluzioni pratiche concrete, ho deciso di creare una libreria (parte di finagle) da solo: finagle-mysql . Fondamentalmente decodifica / decodifica richiesta / risposta mysql e usa Finagle / Netty sotto il cofano. Ridimensiona estremamente bene anche con un numero enorme di connessioni.




Il problema è come potrebbe il db notificare al client al termine della query. Uno sarebbe (ad es.) Che Oracle utilizzi la funzione "Notifica di modifica del risultato della query del database" e riceva una notifica quando cambiano i dati del db. Questo vale per le query SQL che modificano i dati del db. Per le query di sola lettura non funzionerebbe. D'altra parte non sono sicuro che rendere le connessioni asincrone sia una buona idea poiché stabilirle è costoso. Ovviamente questa non è una soluzione molto generale. Solo
spunti di

Finagle-mysql usa JDBC?
Saeed Zarinfam,

Risposte:


164

Non capisco come nessuno degli approcci proposti che avvolgono le chiamate JDBC in attori, esecutori o qualsiasi altra cosa possa aiutare qui - qualcuno può chiarire.

Sicuramente il problema di base è che le operazioni JDBC si bloccano sul socket IO. Quando lo fa, blocca il thread che sta funzionando - fine della storia. Qualunque framework di wrapping tu scelga di usare, finirà con un thread tenuto occupato / bloccato per richiesta simultanea.

Se i driver di database sottostanti (MySql?) Offrono un modo per intercettare la creazione del socket (vedi SocketFactory), allora immagino che sarebbe possibile costruire un livello di database guidato dagli eventi asincroni sopra l'API JDBC ma dovremmo incapsulare il tutto il JDBC dietro una facciata guidata dagli eventi, e quella facciata non assomiglierebbe al JDBC (dopo che sarebbe stata guidata dagli eventi). L'elaborazione del database avverrebbe in modo asincrono su un thread diverso dal chiamante e dovresti capire come costruire un gestore delle transazioni che non si basa sull'affinità del thread.

Qualcosa come l'approccio che menziono consentirebbe anche a un singolo thread in background di elaborare un carico di exec JDBC simultanei. In pratica, avresti probabilmente eseguito un pool di thread per utilizzare più core.

(Ovviamente non sto commentando la logica della domanda originale, ma solo le risposte che implicano che la concorrenza in uno scenario con socket IO bloccante è possibile senza l'utente di un modello di selezione - più semplice solo per elaborare la tipica concorrenza JDBC e mettere in un pool di connessioni della giusta dimensione).


Sembra che MySql probabilmente faccia qualcosa sulla falsariga che sto suggerendo --- http://code.google.com/p/async-mysql-connector/wiki/UsageExample


1
L'uso di Akka non effettua chiamate asincrone a DB relazionali. Ti permette di eseguirli facilmente su un sacco di thread dedicati per l'accesso al DB. In questo modo non si annulla l'intero sito quando il sito non risponde perché si sono sempre fatte chiamate asincrone nel livello di servizio al livello DAO con promesse e i thread del server Web sono separati dal resto dell'applicazione.
Onur,

Gli attori non sono le uniche soluzioni alternative (ad es. Micro-servizi e http asincrono, che riduciamo a migliaia al secondo), e non sarei così veloce da liquidarli come non asincroni dal punto di vista del cliente. Se il traffico di thread dell'interfaccia utente 1k entra nel tuo sistema e solo 10 thread vengono bloccati sul DB, mentre 990 "messaggi" (o qualcosa di simile) vengono messi in coda in memoria senza bloccare alcun thread dell'interfaccia utente 1k (che probabilmente verrà rilasciato). .. non è quello che è richiesto? Mi piacerebbe vedere il vero JDBC asincrono, ma ciò non significa che non ci siano soluzioni estremamente praticabili nel frattempo.
Greg Pendlebury,

42

È impossibile effettuare una chiamata asincrona al database tramite JDBC, ma è possibile effettuare chiamate asincrone a JDBC con Actors (ad esempio, attore effettua chiamate al DB tramite JDBC e invia messaggi a terzi, quando le chiamate sono terminate), o, se ti piace CPS, con futures pipeline (promesse) (una buona implementazione è Scalaz Promises )

Non ritengo che l'uso di un pool di thread sia una soluzione perché non si adatta, nel caso di richieste simultanee pesanti ciò genererà un numero molto elevato di thread.

Gli attori Scala per impostazione predefinita sono basati su eventi (non basati su thread): la pianificazione della continuazione consente di creare milioni di attori su una configurazione JVM standard.

Se hai come target Java, Akka Framework è un'implementazione del modello Actor che ha una buona API sia per Java che per Scala.


A parte questo, la natura sincrona di JDBC ha perfettamente senso per me. Il costo di una sessione del database è di gran lunga superiore al costo del thread Java bloccato (in primo piano o in background) e in attesa di una risposta. Se le tue query vengono eseguite così a lungo che le capacità di un servizio di esecuzione (o il wrapping di framework di concorrenza Actor / fork-join / promise) non sono sufficienti per te (e stai consumando troppi thread), dovresti prima di tutto pensare al tuo caricamento del database. Normalmente la risposta da un database ritorna molto velocemente e un servizio di esecuzione eseguito da un pool di thread fisso è una soluzione abbastanza buona. Se hai troppe query di lunga durata, dovresti considerare l'elaborazione (pre) anticipata, come il ricalcolo notturno dei dati o qualcosa del genere.


2
@Victor, ogni attore che lavora in parallelo su un'operazione di blocco (JDBC) verrà eseguito su un thread separato che Steve sta cercando di evitare
Vasil Remeniuk,

36
L'approccio attore richiede ancora un thread per transazione di database attiva, mentre la transazione è in corso, quindi questa non è davvero una soluzione al problema del PO a meno che tu non sia disposto a vincolare il numero di transazioni di database parallele e attendere che alcune operazioni di database "asincrone" attendano per alcuni già in esecuzione quelli per finire e liberare un thread. Questa non è una cattiva idea, tuttavia - il database potrebbe sovraccaricarsi se si aprono troppe connessioni - quindi sarà d'aiuto mettere la transazione del database in una coda per l'elaborazione invece di bloccare il thread di elaborazione della richiesta http.
Dobes Vandermeer,

8
La soluzione basata sull'attore sta ancora bloccando il thread. Non dire che non è possibile eseguire la chiamata asincrona jdbc, ci sono librerie open source sperimentali che tentano di implementare jdbc asincrono.

6
+1 "Il costo di una sessione del database è di gran lunga superiore al costo del thread Java bloccato"
Paul Draper

1
Per le costose chiamate DB di solito non c'è un problema così grande. È quando la chiamata è banale che l'overhead di rete diventa un problema. Se si desidera effettuare 100 query, che richiedono 1 ms sul DB ciascuna, ma l'overhead di rete è di 200 ms, occorreranno più di 20 secondi in modo sincrono, ma occorrerebbero 300 ms in modo asincrono.
Morten,

12

Forse potresti usare un sistema di messaggistica asincrono JMS, che si adatta abbastanza bene, IMHO:

  • Invia un messaggio a una coda, in cui gli abbonati accetteranno il messaggio ed eseguiranno il processo SQL. Il processo principale continuerà a essere eseguito e ad accettare o inviare nuove richieste.

  • Al termine del processo SQL, è possibile eseguire la procedura opposta: inviare un messaggio a ResponseQueue con il risultato del processo e un ascoltatore sul lato client lo accetta ed esegue il codice di richiamata.


7

Non esiste un supporto diretto in JDBC ma hai più opzioni come MDB, Executors da Java 5.

"Non ritengo che l'utilizzo di un pool di thread sia una soluzione perché non si adatta, nel caso di richieste simultanee pesanti ciò genererà un numero molto elevato di thread".

Sono curioso di sapere perché un pool limitato di thread non si ridimensionerà? È un pool non thread-per-richiesta per generare un thread per ogni richiesta. Ho usato questo per un po 'di tempo su una webapp di carico pesante e finora non abbiamo riscontrato alcun problema.


Penso che l'argomento principale contro i thread sia che in pratica esci dai vincoli dei contenitori Java standard, quindi perdi il clustering gestito dal contenitore e fallisci sulle capacità, anche se potresti farne uno tuo o usare qualcosa come Terracotta.
Mezmo,

3
possiamo attingere ai sondaggi di thread gestiti dal server delle app usando i gestori del lavoro. websphere, weblogic e glassfish lo supportano
Aravind Yarram,


4

Come menzionato in altre risposte, l'API JDBC non è asincrona per sua natura.
Tuttavia, se riesci a convivere con un sottoinsieme delle operazioni e un'API diversa ci sono soluzioni. Un esempio è https://github.com/jasync-sql/jasync-sql che funziona per MySQL e PostgreSQL.


3

Il progetto Ajdbc sembra rispondere a questo problema http://code.google.com/p/adbcj/

Al momento ci sono 2 driver sperimentali nativi asincroni per mysql e postgresql.


Vorrei avere questo approccio pronto. JDBC si è evoluto molto dall'inizio (iteratori, modelli, procedure preparate), ma questo approccio asincrono non è mai stato implementato. Sarebbe particolarmente interessante per le operazioni di scrittura (Inserisci, Aggiorna, Elimina) e specialmente per quei batch batch pesanti che tutti noi affrontiamo. A mio avviso, qualsiasi tipo di approccio basato sul client (Pooling, Actor, Scheduling, Messaging ...) porterebbe a piccoli vantaggi in termini di utilizzo delle risorse (probabilmente alcuni guadagni in termini di throughput o latenza).
Jaime Casero,

Vecchio e abbandonato, solo due tipi di dati supportati e nemmeno vicini alla produzione pronti. Sfortunatamente :(
Aaron Zinman il

Il numero 1 di questa libreria riguarda il sito Web non disponibile . Ha più di un anno. Sospetto che questa biblioteca sia piuttosto morta.
Lukas Eder,

3

Una vecchia domanda, ma qualche informazione in più. Non è possibile che JDBC emetta richieste asincrone al database stesso, a meno che un fornitore non fornisca un'estensione a JDBC e un wrapper con cui gestire JDBC. Detto questo, è possibile racchiudere JDBC stesso con una coda di elaborazione e implementare una logica in grado di elaborare la coda su una o più connessioni separate. Un vantaggio di questo per alcuni tipi di chiamate è che la logica, se sotto carico abbastanza pesante, potrebbe convertire le chiamate in batch JDBC per l'elaborazione, il che può accelerare significativamente la logica. Ciò è molto utile per le chiamate in cui vengono inseriti i dati e il risultato effettivo deve essere registrato solo in caso di errore. Un ottimo esempio di ciò è se vengono eseguiti inserti per registrare l'attività dell'utente. L'applicazione ha vinto '

Come nota a margine, un prodotto sul mercato offre un approccio guidato dalle politiche per consentire chiamate asincrone come quelle che ho descritto per essere fatte in modo asincrono ( http://www.heimdalldata.com/ ). Disclaimer: sono cofondatore di questa azienda. Consente di applicare espressioni regolari alle richieste di trasformazione dei dati come inserire / aggiornare / eliminare per qualsiasi origine dati JDBC e raggrupparle automaticamente per l'elaborazione. Se utilizzato con MySQL e l'opzione rewriteBatchedStatements ( MySQL e JDBC con rewriteBatchedStatements = true ), ciò può ridurre significativamente il carico complessivo sul database.


Ciò significa comunque che JDBC dovrebbe avere almeno un thread separato. Che dire di framework e stack a thread singolo ma ancora basati su callback (viene in mente nodejs)? Sai come gestiscono le chiamate JDBC?
yuranos,

3

Hai tre opzioni secondo me:

  1. Utilizzare una coda simultanea per distribuire i messaggi su un numero limitato e fisso di thread. Quindi se hai 1000 connessioni avrai 4 thread, non 1000 thread.
  2. Eseguire l'accesso al database su un altro nodo (ovvero un altro processo o macchina) e fare in modo che il client del database effettui chiamate di rete asincrone a quel nodo.
  3. Implementare un vero sistema distribuito tramite messaggi asincroni. Per questo avrai bisogno di una coda di messaggistica come CoralMQ o Tibco.

Diclaimer: sono uno degli sviluppatori di CoralMQ.


3

È stata sviluppata una soluzione per rendere possibile la connettività reattiva con database relazionali standard.

Le persone che desiderano ridimensionare mantenendo l'utilizzo dei database relazionali sono tagliate fuori dalla programmazione reattiva a causa degli standard esistenti basati sul blocco dell'I / O. R2DBC specifica una nuova API che consente il codice reattivo che funziona in modo efficiente con i database relazionali.

R2DBC è una specifica progettata da zero per la programmazione reattiva con database SQL che definiscono una SPI non bloccante per gli implementatori di driver di database e gli autori di librerie client. I driver R2DBC implementano completamente il protocollo del filo del database su un livello I / O non bloccante.

Sito Web di R2DBC

GitHub di R2DBC

Feature Matrix

inserisci qui la descrizione dell'immagine


2

Gli esecutori Java 5.0 potrebbero tornare utili.

È possibile disporre di un numero fisso di thread per gestire operazioni di lunga durata. E invece di Runnablete puoi usare Callable, che restituisce un risultato. Il risultato è incapsulato in un Future<ReturnType>oggetto, quindi puoi ottenerlo quando è tornato.



2

Un'idea folle: potresti usare un modello Iteratee sul risultato JBDC Impostato in un po 'di Future / Promise

Hammersmith lo fa per MongoDB .


1

Sto solo pensando idee qui. Perché non è possibile avere un pool di connessioni al database con ognuna con un thread. Ogni thread ha accesso a una coda. Quando vuoi fare una query che richiede molto tempo, puoi mettere in coda e poi uno dei thread lo raccoglierà e lo gestirà. Non avrai mai troppi thread perché il numero dei thread è limitato.

Modifica: o meglio ancora, solo un numero di thread. Quando un thread vede qualcosa in una coda, chiede una connessione dal pool e lo gestisce.


1

La libreria commons-dbutils ha il supporto per un AsyncQueryRunneroggetto da te fornito ExecutorServicee restituisce a Future. Vale la pena dare un'occhiata perché è semplice da usare e assicurati di non perdere risorse.


1

Se sei interessato alle API di database asincrone per Java, dovresti sapere che esiste una nuova iniziativa per creare un set di API standard basate su CompletableFuture e lambdas. Esiste anche un'implementazione di queste API su JDBC che può essere utilizzata per esercitarsi con queste API: https://github.com/oracle/oracle-db-examples/tree/master/java/AoJ JavaDoc è menzionato nel README di il progetto github.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.