¿Es posible la llamada asíncrona jdbc?

158

Me pregunto si hay una manera de hacer llamadas asincrónicas a una base de datos.

Por ejemplo, imagine que tengo una gran solicitud que toma mucho tiempo procesar, quiero enviar la solicitud y recibir una notificación cuando la solicitud devuelva un valor (al pasar un Listener / callback o algo así). No quiero bloquear la espera de que la base de datos responda.

No considero que el uso de un grupo de subprocesos sea una solución porque no se escala, en el caso de solicitudes concurrentes pesadas esto generará una gran cantidad de subprocesos.

Estamos enfrentando este tipo de problema con los servidores de red y hemos encontrado soluciones al usar la llamada al sistema select / poll / epoll para evitar tener un hilo por conexión. Me pregunto cómo tener una función similar con la solicitud de base de datos.

Nota: Soy consciente de que usar un FixedThreadPool puede ser una buena solución, pero me sorprende que nadie haya desarrollado un sistema realmente asíncrono (sin el uso de hilo adicional).

** Actualización **
Debido a la falta de soluciones prácticas reales, decidí crear una biblioteca (parte de finagle): finagle-mysql . Básicamente decodifica / decodifica la solicitud / respuesta mysql, y usa Finagle / Netty debajo del capó. Se escala extremadamente bien incluso con una gran cantidad de conexiones.

Steve Gury
fuente
1
Ver también github.com/mauricio/postgresql-async
Daniel Worthington-Bodart
El problema es cómo podría db notificar al cliente cuando finalice la consulta. Una sería (p. Ej.) Que Oracle use la función "Notificación de cambio de resultado de consulta de base de datos" y reciba una notificación cuando se modifiquen los datos db. Esto se aplica a las consultas SQL que modifican los datos db. Para consultas de solo lectura, esto no funcionaría. Por otro lado, no estoy seguro de que hacer conexiones asíncronas sería una buena idea ya que establecerlas es costoso. Por supuesto, esta no es una solución muy general. Sólo comida para el pensamiento ...
Mike Argyriou
¿Finagle-mysql usa JDBC?
Saeed Zarinfam

Respuestas:

164

No entiendo cómo cualquiera de los enfoques propuestos que envuelven las llamadas JDBC en actores, ejecutores o cualquier otra cosa puede ayudar aquí, ¿alguien puede aclararlo?

Seguramente el problema básico es que las operaciones JDBC se bloquean en el socket IO. Cuando hace esto, bloquea el hilo en ejecución - final de la historia. Cualquiera que sea el marco de envoltura que elija utilizar, terminará con un subproceso ocupado / bloqueado por solicitud simultánea.

Si los controladores de la base de datos subyacentes (MySql?) Ofrecen un medio para interceptar la creación del socket (vea SocketFactory), entonces imagino que sería posible construir una capa de base de datos controlada por eventos asíncronos en la parte superior de la API JDBC, pero tendríamos que encapsular el JDBC completo detrás de una fachada impulsada por eventos, y esa fachada no se vería como JDBC (después de que sería impulsada por eventos). El procesamiento de la base de datos sucedería de forma asíncrona en un subproceso diferente al que realiza la llamada, y tendría que averiguar cómo construir un administrador de transacciones que no dependa de la afinidad del subproceso.

Algo así como el enfoque que menciono permitiría que incluso un solo subproceso en segundo plano procese una carga de ejecutivos JDBC concurrentes. En la práctica, probablemente ejecute un grupo de subprocesos para utilizar múltiples núcleos.

(Por supuesto, no estoy comentando sobre la lógica de la pregunta original, solo las respuestas que implican que la concurrencia en un escenario con bloqueo de socket IO es posible sin el usuario de un patrón selector, más simple solo para resolver su concurrencia JDBC típica y poner en un grupo de conexiones del tamaño correcto).


Parece que MySql probablemente hace algo similar a lo que sugiero --- http://code.google.com/p/async-mysql-connector/wiki/UsageExample

johnlon
fuente
1
El uso de Akka no realiza llamadas a bases de datos relacionales asincrónicas. Le permite ejecutarlos en un montón de subprocesos dedicados para acceder a la base de datos fácilmente. De esta manera, no se desactiva todo el sitio cuando el sitio deja de responder porque siempre ha estado haciendo llamadas asíncronas en la capa de servicio a la capa DAO con promesas y los hilos de su servidor web están separados del resto de su aplicación.
Onur
Los actores no son las únicas soluciones (por ejemplo, microservicios y http asíncrono, que escalamos a miles por segundo), y no sería tan rápido descartarlos como no asíncronos desde la perspectiva del cliente. Si el tráfico de subprocesos de 1k UI ingresa a su sistema y solo 10 subprocesos están bloqueados en la base de datos, mientras que 990 'mensajes' (o algo similar) se ponen en cola en la memoria sin bloquear ninguno de los subprocesos de 1k UI (que probablemente se liberarán). ¿No es eso lo que se requiere? Me encantaría ver un verdadero JDBC asíncrono, pero eso no significa que no haya soluciones alternativas extremadamente viables en el ínterin.
Greg Pendlebury
42

Es imposible realizar una llamada asincrónica a la base de datos a través de JDBC, pero puede realizar llamadas asincrónicas a JDBC con Actores (por ejemplo, el actor realiza llamadas a la base de datos a través de JDBC y envía mensajes a terceros, cuando las llamadas terminan), o, si le gusta CPS, con futuros canalizados (promesas) (una buena implementación es Scalaz Promises )

No considero que el uso de un grupo de subprocesos sea una solución porque no se escala, en el caso de solicitudes concurrentes pesadas esto generará una gran cantidad de subprocesos.

Los actores de Scala por defecto están basados ​​en eventos (no basados ​​en hilos): la programación de continuación permite crear millones de actores en una configuración JVM estándar.

Si está apuntando a Java, Akka Framework es una implementación de modelo de actor que tiene una buena API tanto para Java como para Scala.


Aparte de eso, la naturaleza sincrónica de JDBC tiene mucho sentido para mí. El costo de una sesión de base de datos es mucho mayor que el costo del bloqueo del hilo de Java (ya sea en primer o segundo plano) y esperando una respuesta. Si sus consultas se ejecutan durante tanto tiempo que las capacidades de un servicio de ejecutor (o envolviendo los marcos de concurrencia Actor / fork-join / promise) no son suficientes para usted (y está consumiendo demasiados hilos), primero debe pensar en su carga de la base de datos. Normalmente, la respuesta de una base de datos regresa muy rápido, y un servicio ejecutor respaldado con un grupo de subprocesos fijos es una solución lo suficientemente buena. Si tiene demasiadas consultas de larga duración, debe considerar el procesamiento previo (previo), como el recálculo nocturno de los datos o algo así.

Vasil Remeniuk
fuente
2
@Victor, cada actor que trabaje en paralelo en una operación de bloqueo (JDBC) se ejecutará en un hilo separado que Steve está tratando de evitar
Vasil Remeniuk el
36
El enfoque de actor todavía requiere un subproceso por transacción de base de datos activa, mientras la transacción continúa, por lo que no es realmente una solución al problema del OP a menos que esté dispuesto a restringir el número de transacciones de base de datos paralelas y hacer que algunas operaciones de base de datos "asíncronas" esperen para algunos que ya están ejecutando para terminar y liberar un hilo. Sin embargo, esta no es una mala idea: la base de datos puede sobrecargarse si abre demasiadas conexiones, por lo que será útil colocar la transacción de la base de datos en una cola para procesarla en lugar de bloquear su hilo de procesamiento de solicitudes http.
Dobes Vandermeer
8
La solución basada en el actor sigue bloqueando el hilo. No diga que no es posible ejecutar una llamada async jdbc, hay bibliotecas experimentales de código abierto que intentan implementar async jdbc.
66
+1 "El costo de una sesión de base de datos es mucho más alto que el costo del bloqueo del hilo de Java"
Paul Draper
1
Para las llamadas de DB costosas, generalmente no hay un problema tan grande. Es cuando la llamada es trivial que la sobrecarga de la red se convierte en un problema. Si desea realizar 100 consultas, que toman 1 ms en la base de datos cada una, pero la sobrecarga de la red es de 200 ms, entonces tomará más de 20 segundos sincrónicamente, pero tomaría 300 ms de forma asincrónica.
morten
12

Tal vez podría usar un sistema de mensajería asíncrona JMS, que escala bastante bien, en mi humilde opinión:

  • Envíe un mensaje a una cola, donde los suscriptores aceptarán el mensaje y ejecutarán el proceso SQL. Su proceso principal continuará ejecutándose y aceptando o enviando nuevas solicitudes.

  • Cuando finaliza el proceso SQL, puede ejecutarlo de la manera opuesta: envíe un mensaje a ResponseQueue con el resultado del proceso, y un escucha del lado del cliente lo acepta y ejecuta el código de devolución de llamada.

Tomás Narros
fuente
7

No hay soporte directo en JDBC pero tiene múltiples opciones como MDB, Executors from Java 5.

"No considero que el uso de un grupo de subprocesos sea una solución porque no se escala, en el caso de solicitudes concurrentes pesadas esto generará una gran cantidad de subprocesos".

Tengo curiosidad por qué un grupo limitado de hilos no se escalará. Es un grupo no hilo por solicitud para generar un hilo por cada solicitud. He estado usando esto durante bastante tiempo en una aplicación web de carga pesada y hasta ahora no hemos visto ningún problema.

Aravind Yarram
fuente
Creo que el argumento principal en contra de los subprocesos es que básicamente estás fuera de las restricciones estándar de los contenedores Java, por lo que pierdes la agrupación administrada del contenedor y las capacidades de conmutación por error, aunque puedes rodar el tuyo o usar algo como Terracota.
mezmo
3
podemos aprovechar las encuestas de hilos administrados por el servidor de aplicaciones mediante el uso de gerentes de trabajo. websphere, weblogic y glassfish lo respaldan
Aravind Yarram el
4

Como se menciona en otras respuestas, la API JDBC no es asíncrona por su naturaleza.
Sin embargo, si puede vivir con un subconjunto de operaciones y una API diferente, existen soluciones. Un ejemplo es https://github.com/jasync-sql/jasync-sql que funciona para MySQL y PostgreSQL.

oshai
fuente
3

El proyecto Ajdbc parece responder a este problema http://code.google.com/p/adbcj/

Actualmente hay 2 controladores asíncronos nativos experimentales para mysql y postgresql.

Sebastien
fuente
Me gustaría tener este enfoque listo. JDBC ha evolucionado mucho desde el principio (iteradores, plantillas, procedimientos preparados), pero este enfoque asincrónico nunca se ha implementado. Sería particularmente interesante para las operaciones de escritura (Insertar, Actualizar, Eliminar), y especialmente aquellos TX de lotes pesados ​​que todos enfrentamos. En mi opinión, cualquier tipo de enfoque basado en el cliente (Pooling, Actor, Scheduling, Messaging ...) conduciría a pequeñas recompensas en términos de uso de recursos (probablemente algunas ganancias en rendimiento o latencia).
Jaime Casero
Antiguo y abandonado, solo se admiten dos tipos de datos y ni siquiera están listos para la producción. Desafortunadamente :(
Aaron Zinman
El número 1 de esta biblioteca trata sobre el sitio web que no está disponible . Tiene más de un año. Sospecho que esta biblioteca está bastante muerta.
Lukas Eder
3

Una vieja pregunta, pero algo más de información. No es posible que JDBC emita solicitudes asincrónicas a la base de datos, a menos que un proveedor proporcione una extensión a JDBC y un contenedor para manejar JDBC. Dicho esto, es posible envolver JDBC con una cola de procesamiento e implementar una lógica que pueda procesar fuera de la cola en una o más conexiones separadas. Una ventaja de esto para algunos tipos de llamadas es que la lógica, si está bajo una carga lo suficientemente pesada, podría convertir las llamadas en lotes JDBC para su procesamiento, lo que puede acelerar la lógica significativamente. Esto es más útil para llamadas donde se están insertando datos, y el resultado real solo necesita registrarse si hay un error. Un gran ejemplo de esto es si se realizan inserciones para registrar la actividad del usuario. La aplicación ganó '

Como nota al margen, un producto en el mercado proporciona un enfoque basado en políticas para permitir que las llamadas asincrónicas como las que describí se realicen de forma asincrónica ( http://www.heimdalldata.com/ ). Descargo de responsabilidad: soy cofundador de esta empresa. Permite que se apliquen expresiones regulares a las solicitudes de transformación de datos, como insertar / actualizar / eliminar para cualquier fuente de datos JDBC, y las agrupará automáticamente para su procesamiento. Cuando se usa con MySQL y la opción rewriteBatchedStatements ( MySQL y JDBC con rewriteBatchedStatements = true ), esto puede reducir significativamente la carga general en la base de datos.

Erik Brandsberg
fuente
Pero esto todavía significa que JDBC debería tener al menos un hilo separado. ¿Qué pasa con los frameworks y las pilas que son de un solo subproceso pero que aún se basan en la devolución de llamadas (me viene a la mente nodejs)? ¿Sabes cómo gestionan las llamadas JDBC?
yuranos
3

Tienes tres opciones en mi opinión:

  1. Use una cola concurrente para distribuir mensajes en un número pequeño y fijo de hilos. Entonces, si tiene 1000 conexiones, tendrá 4 hilos, no 1000 hilos.
  2. Acceda a la base de datos en otro nodo (es decir, otro proceso o máquina) y haga que su cliente de base de datos realice llamadas de red asincrónicas a ese nodo.
  3. Implemente un verdadero sistema distribuido a través de mensajes asincrónicos. Para eso necesitará una cola de mensajes como CoralMQ o Tibco.

Diclaimer: Soy uno de los desarrolladores de CoralMQ.

rdalmeida
fuente
3

Se está desarrollando una solución para hacer posible la conectividad reactiva con bases de datos relacionales estándar.

Las personas que desean escalar mientras conservan el uso de bases de datos relacionales están aisladas de la programación reactiva debido a los estándares existentes basados ​​en el bloqueo de E / S. R2DBC especifica una nueva API que permite código reactivo que funciona de manera eficiente con bases de datos relacionales.

R2DBC es una especificación diseñada desde cero para la programación reactiva con bases de datos SQL que definen un SPI sin bloqueo para implementadores de controladores de bases de datos y autores de bibliotecas de clientes. Los controladores R2DBC implementan completamente el protocolo de conexión de la base de datos sobre una capa de E / S sin bloqueo.

Sitio web de R2DBC

GitHub de R2DBC

Matriz de funciones

ingrese la descripción de la imagen aquí

Yassin Hajaj
fuente
2

Los ejecutores de Java 5.0 pueden ser útiles.

Puede tener un número fijo de subprocesos para manejar operaciones de larga duración. Y en lugar de Runnableque pueda usar Callable, que devuelven un resultado. El resultado se encapsula en un Future<ReturnType>objeto, por lo que puede obtenerlo cuando regrese.

Bozho
fuente
2

Solo una idea loca: podría usar un patrón Iteratee sobre el resultado de JBDC Conjunto envuelto en algún Futuro / Promesa

Hammersmith hace eso por MongoDB .

jwinandy
fuente
1

Solo estoy pensando ideas aquí. ¿Por qué no podría tener un grupo de conexiones de bases de datos con cada una que tenga un hilo? Cada hilo tiene acceso a una cola. Cuando desee hacer una consulta que lleve mucho tiempo, puede ponerla en la cola y luego uno de los hilos la recogerá y la manejará. Nunca tendrás demasiados hilos porque el número de tus hilos está limitado.

Editar: O mejor aún, solo una serie de hilos. Cuando un hilo ve algo en una cola, solicita una conexión del grupo y lo maneja.

Amir Raminfar
fuente
1

La biblioteca commons-dbutils tiene soporte para una a la AsyncQueryRunnerque proporciona un ay ExecutorServicedevuelve un Future. Vale la pena echarle un vistazo, ya que es fácil de usar y garantiza que no perderá recursos.

William Speirs
fuente
1

Si está interesado en las API de base de datos asíncronas para Java, debe saber que existe una nueva iniciativa para crear un conjunto de API estándar basadas en CompletableFuture y lambdas. También hay una implementación de estas API sobre JDBC que se puede usar para practicar estas API: https://github.com/oracle/oracle-db-examples/tree/master/java/AoJ El JavaDoc se menciona en el archivo README de El proyecto Github.

Jean de Lavarene
fuente