A chamada jdbc assíncrona é possível?

158

Gostaria de saber se existe uma maneira de fazer chamadas assíncronas para um banco de dados?

Por exemplo, imagine que eu tenho uma grande solicitação que leva muito tempo para processar. Quero enviar a solicitação e receber uma notificação quando a solicitação retornar um valor (passando um ouvinte / retorno de chamada ou algo assim). Não quero bloquear a espera pela resposta do banco de dados.

Não considero que o uso de um pool de threads seja uma solução, pois não é dimensionável; no caso de solicitações simultâneas pesadas, isso gera um número muito grande de threads.

Estamos enfrentando esse tipo de problema com os servidores de rede e encontramos soluções usando a chamada de sistema select / poll / epoll para evitar ter um encadeamento por conexão. Eu só estou querendo saber como ter um recurso semelhante com a solicitação de banco de dados?

Nota: Estou ciente de que o uso de um FixedThreadPool pode ser uma boa solução, mas estou surpreso que ninguém tenha desenvolvido um sistema realmente assíncrono (sem o uso de encadeamento extra).

** Atualização **
Devido à falta de soluções práticas reais, decidi criar uma biblioteca (parte do finagle): finagle-mysql . Basicamente, decodifica / decodifica a solicitação / resposta do mysql e usa o Finagle / Netty sob o capô. Escala extremamente bem, mesmo com um grande número de conexões.

Steve Gury
fonte
1
Veja também github.com/mauricio/postgresql-async
Daniel Worthington-Bodart
O problema é como o banco de dados pode notificar o cliente quando a consulta é concluída. Um seria (por exemplo) para a Oracle usar o recurso "Notificação de alteração no resultado da consulta ao banco de dados" e ser notificado quando os dados do banco de dados forem alterados. Isso se aplica a consultas SQL que modificam os dados do banco de dados. Para consultas somente leitura, isso não funcionaria. Por outro lado, não tenho certeza de que fazer conexões assíncronas seria uma boa idéia, pois estabelecê-las é caro. É claro que essa não é uma solução muito geral. Apenas comida para reflexão ...
Mike Argyriou
O finagle-mysql usa JDBC?
Saeed Zarinfam

Respostas:

164

Não entendo como qualquer uma das abordagens propostas que envolvem chamadas JDBC em atores, executores ou qualquer outra coisa pode ajudar aqui - alguém pode esclarecer.

Certamente o problema básico é que as operações JDBC são bloqueadas no soquete IO. Quando faz isso, ele bloqueia o Thread no final da história. Qualquer que seja a estrutura de agrupamento que você escolher, ela acabará com um encadeamento sendo ocupado / bloqueado por solicitação simultânea.

Se os drivers de banco de dados subjacentes (MySql?) Oferecerem um meio de interceptar a criação do soquete (consulte SocketFactory), imagino que seria possível criar uma camada de banco de dados assíncrona orientada a eventos sobre a API JDBC, mas teríamos que encapsular o JDBC inteiro atrás de uma fachada orientada a eventos, e essa fachada não se pareceria com JDBC (depois que seria orientada a eventos). O processamento do banco de dados aconteceria de maneira assíncrona em um encadeamento diferente do chamador, e você teria que descobrir como criar um gerenciador de transações que não depende da afinidade do encadeamento.

Algo parecido com a abordagem mencionada permitiria que mesmo um único encadeamento em segundo plano processasse uma carga de executivos JDBC simultâneos. Na prática, você provavelmente executaria um pool de threads para usar vários núcleos.

(É claro que não estou comentando a lógica da pergunta original, apenas as respostas que implicam que a simultaneidade em um cenário com IO de soquete de bloqueio é possível sem o usuário de um padrão seletor - é mais simples trabalhar com sua simultaneidade JDBC típica e colocar em um conjunto de conexões do tamanho certo).


Parece que o MySql provavelmente faz algo parecido com o que estou sugerindo --- http://code.google.com/p/async-mysql-connector/wiki/UsageExample

johnlon
fonte
1
O uso do Akka não faz chamadas para os bancos de dados relacionais assíncronos. Permite executá-los em vários threads dedicados para acesso ao banco de dados facilmente. Dessa forma, você não desativa o site inteiro quando o site fica sem resposta, porque você sempre fazia chamadas assíncronas na camada de serviço para a camada DAO com promessas e os threads do servidor da Web são separados do restante do aplicativo.
Onur
Os atores não são as únicas soluções alternativas (por exemplo, microsserviços e http assíncrono, que aumentamos para milhares por segundo), e eu não seria tão rápido em descartá-los por não serem assíncronos da perspectiva do cliente. Se o tráfego de threads de 1k da interface do usuário entrar no seu sistema e apenas 10 threads forem bloqueados no banco de dados, enquanto 990 'mensagens' (ou algo semelhante) serão colocadas na fila na memória sem bloquear nenhum dos threads de 1k da interface do usuário (que provavelmente serão liberados). .. não é isso que é necessário? Eu adoraria ver o JDBC assíncrono verdadeiro, mas isso não significa que não haja soluções extremamente viáveis ​​nesse ínterim.
perfil completo de Greg Pendlebury
42

É impossível fazer uma chamada assíncrona para o banco de dados via JDBC, mas você pode fazer chamadas assíncronas para o JDBC com atores (por exemplo, o ator faz chamadas para o banco de dados via JDBC e envia mensagens para terceiros quando as chamadas terminam), ou, se você gosta do CPS, com futuros em pipeline (promessas) (uma boa implementação é Scalaz Promises )

Não considero que o uso de um pool de threads seja uma solução, pois não é dimensionável; no caso de solicitações simultâneas pesadas, isso gera um número muito grande de threads.

Os atores Scala, por padrão, são baseados em eventos (não em threads) - o agendamento de continuação permite criar milhões de atores em uma configuração JVM padrão.

Se você está direcionando para Java, o Akka Framework é uma implementação de modelo de ator que possui uma boa API para Java e Scala.


Além disso, a natureza síncrona do JDBC faz todo o sentido para mim. O custo de uma sessão de banco de dados é muito superior ao custo do encadeamento Java sendo bloqueado (em primeiro ou segundo plano) e aguardando uma resposta. Se suas consultas forem executadas por tanto tempo que os recursos de um serviço executor (ou agrupar estruturas de simultaneidade Actor / fork-join / promessa) não são suficientes para você (e você está consumindo muitos threads), primeiro pense em seu carga de banco de dados. Normalmente, a resposta de um banco de dados volta muito rapidamente, e um serviço executor suportado com um pool de threads fixo é uma solução boa o suficiente. Se você tiver muitas consultas de longa execução, considere o processamento inicial (pré-) - como recálculo noturno dos dados ou algo assim.

Vasil Remeniuk
fonte
2
@Victor, cada ator trabalhando em um paralelo em uma operação de bloqueio (JDBC) será executado em um segmento separado que Steve está tentando evitar
Vasil Remeniuk
36
A abordagem do ator ainda requer um encadeamento por transação ativa do banco de dados, enquanto a transação está em andamento, portanto, essa não é realmente uma solução para o problema do OP, a menos que você esteja disposto a restringir o número de transações paralelas do banco de dados e esperar algumas operações de banco de dados "assíncronas" para alguns já em execução para finalizar e liberar um thread. Porém, essa não é uma má idéia - o banco de dados pode ficar sobrecarregado se você abrir muitas conexões -, portanto, colocar a transação do banco de dados em uma fila para processamento em vez de bloquear o encadeamento de processamento de solicitação http ajudará.
Dobes Vandermeer
8
A solução baseada em ator ainda está bloqueando o encadeamento. Não diga que não é possível executar uma chamada assíncrona jdbc, existem bibliotecas experimentais de código aberto que tentam implementar async jdbc.
6
+1 "O custo de uma sessão de banco de dados é muito maior do que o custo do fio Java sendo bloqueado"
Paul Draper
1
Para chamadas de banco de dados caras, geralmente não há um problema tão grande. É quando a chamada é trivial que a sobrecarga da rede se torna um problema. Se você quiser fazer 100 consultas, que levam 1 ms no banco de dados cada, mas a sobrecarga da rede é de 200 ms, levará mais de 20 segundos de forma síncrona, mas levará 300 ms de forma assíncrona.
morten
12

Talvez você possa usar um sistema de mensagens assíncrono JMS, que escala muito bem, IMHO:

  • Envie uma mensagem para uma Fila, na qual os assinantes aceitarão a mensagem e execute o processo SQL. Seu processo principal continuará em execução e aceitar ou enviar novas solicitações.

  • Quando o processo SQL termina, você pode executar o caminho oposto: envie uma mensagem para um ResponseQueue com o resultado do processo, e um ouvinte no lado do cliente o aceitará e executará o código de retorno de chamada.

Tomas Narros
fonte
7

Não há suporte direto no JDBC, mas você tem várias opções como MDB, Executors from Java 5.

"Não considero que o uso de um pool de threads seja uma solução, porque não é escalável. No caso de solicitações simultâneas pesadas, isso gera um número muito grande de threads".

Estou curioso por que um pool limitado de threads não será dimensionado? É um pool não encadeado por solicitação para gerar um encadeamento por cada solicitação. Eu tenho usado isso por algum tempo em um webapp de carga pesada e ainda não vimos nenhum problema.

Aravind Yarram
fonte
Eu acho que o principal argumento contra threads é que você está basicamente fora de qualquer restrição padrão de contêineres Java, para que você perca os recursos de cluster gerenciado por contêiner e failover, embora possa rolar sozinho ou usar algo como Terracotta.
Novelas
3
podemos acessar as pesquisas de threads gerenciadas pelo servidor de aplicativos usando gerentes de trabalho. websphere, WebLogic e apoio glassfish-lo
Aravind Yarram
4

Conforme mencionado em outras respostas, a API do JDBC não é assíncrona por natureza.
No entanto, se você pode viver com um subconjunto de operações e uma API diferente, existem soluções. Um exemplo é https://github.com/jasync-sql/jasync-sql que funciona para MySQL e PostgreSQL.

oshai
fonte
3

O projeto Ajdbc parece responder a este problema http://code.google.com/p/adbcj/

Atualmente, existem 2 drivers nativos assíncronos experimentais para mysql e postgresql.

Sebastien
fonte
Eu gostaria de ter essa abordagem pronta. O JDBC evoluiu muito desde o início (iteradores, modelos, procedimentos preparados), mas essa abordagem assíncrona nunca foi implementada. Seria particularmente interessante para operações de gravação (Inserir, Atualizar, Excluir) e, especialmente, para o lote pesado TX que todos enfrentamos. Na minha opinião, qualquer tipo de abordagem baseada em cliente (pool, ator, agendamento, mensagens ...) levaria a pequenas recompensas em termos de uso de recursos (provavelmente alguns ganhos em taxa de transferência ou latência).
Jaime Casero
Antigo e abandonado, apenas dois tipos de dados são suportados e nem chegam perto da produção. Infelizmente :(
Aaron Zinman
O problema nº 1 desta biblioteca é sobre o site não estar disponível . É mais de um ano de idade. Suspeito que esta biblioteca esteja morta.
Lukas Eder
3

Uma pergunta antiga, mas mais algumas informações. Não é possível que o JDBC emita solicitações assíncronas para o próprio banco de dados, a menos que um fornecedor forneça uma extensão ao JDBC e um wrapper para lidar com o JDBC. Dito isso, é possível agrupar o próprio JDBC com uma fila de processamento e implementar lógica que possa processar a fila em uma ou mais conexões separadas. Uma vantagem disso para alguns tipos de chamadas é que a lógica, se for carregada o suficiente, pode converter as chamadas em lotes JDBC para processamento, o que pode acelerar significativamente a lógica. Isso é mais útil para chamadas em que os dados estão sendo inseridos e o resultado real precisa ser registrado apenas se houver um erro. Um ótimo exemplo disso é se inserções estão sendo executadas para registrar a atividade do usuário. O aplicativo ganhou '

Como observação lateral, um produto no mercado fornece uma abordagem orientada por políticas para permitir que chamadas assíncronas, como aquelas que descrevi, sejam feitas de maneira assíncrona ( http://www.heimdalldata.com/ ). Disclaimer: Eu sou co-fundador desta empresa. Ele permite que expressões regulares sejam aplicadas a solicitações de transformação de dados, como inserção / atualização / exclusões para qualquer fonte de dados JDBC, e as agrupará automaticamente em lote para processamento. Quando usado com o MySQL e a opção rewriteBatchedStatements ( MySQL e JDBC com rewriteBatchedStatements = true ), isso pode reduzir significativamente a carga geral no banco de dados.

Erik Brandsberg
fonte
Mas isso ainda significa que o JDBC deve ter pelo menos um encadeamento separado. E as estruturas e pilhas que são de thread único, mas ainda baseadas em retorno de chamada (o nodejs vem à mente)? Você sabe como eles gerenciam chamadas JDBC?
yuranos
3

Você tem três opções na minha opinião:

  1. Use uma fila simultânea para distribuir mensagens por um número pequeno e fixo de encadeamentos. Portanto, se você tiver 1000 conexões, terá 4 threads, não 1000 threads.
  2. Faça o acesso ao banco de dados em outro nó (ou seja, outro processo ou máquina) e faça com que seu cliente de banco de dados faça chamadas de rede assíncronas para esse nó.
  3. Implemente um verdadeiro sistema distribuído por meio de mensagens assíncronas. Para isso, você precisará de uma fila de mensagens como CoralMQ ou Tibco.

Diclaimer: Eu sou um dos desenvolvedores do CoralMQ.

rdalmeida
fonte
3

Uma solução está sendo desenvolvida para possibilitar a conectividade reativa com bancos de dados relacionais padrão.

As pessoas que desejam escalar enquanto retêm o uso de bancos de dados relacionais são excluídas da programação reativa devido aos padrões existentes baseados no bloqueio de E / S. O R2DBC especifica uma nova API que permite código reativo que funcione eficientemente com bancos de dados relacionais.

R2DBC é uma especificação projetada desde o início para programação reativa com bancos de dados SQL, definindo uma SPI sem bloqueio para implementadores de drivers de banco de dados e autores de bibliotecas clientes. Os drivers R2DBC implementam totalmente o protocolo de conexão do banco de dados sobre uma camada de E / S sem bloqueio.

Site da R2DBC

GitHub do R2DBC

Matriz de recursos

insira a descrição da imagem aqui

Yassin Hajaj
fonte
2

Os executores do Java 5.0 podem ser úteis.

Você pode ter um número fixo de threads para lidar com operações de longa execução. E em vez de Runnablevocê pode usar Callable, que retornam um resultado. O resultado é encapsulado em um Future<ReturnType>objeto, para que você possa obtê-lo quando voltar.

Bozho
fonte
2

Apenas uma ideia maluca: você pode usar um padrão Iteratee sobre o resultado do JBDC

Hammersmith faz isso para o MongoDB .

jwinandy
fonte
1

Estou apenas pensando idéias aqui. Por que você não pode ter um pool de conexões com o banco de dados, cada um com uma thread. Cada encadeamento tem acesso a uma fila. Quando você quiser fazer uma consulta que demore muito, poderá colocar a fila e, em seguida, um dos threads a buscará e manipulará. Você nunca terá muitos threads porque o número de seus threads é limitado.

Edit: Ou melhor ainda, apenas um número de threads. Quando um thread vê algo em uma fila, solicita uma conexão do pool e lida com isso.

Amir Raminfar
fonte
1

A biblioteca commons-dbutils possui suporte para um para o AsyncQueryRunnerqual você fornece um ExecutorServicepara e retorna a Future. Vale a pena conferir, pois é simples de usar e garante que você não irá vazar recursos.

William Speirs
fonte
1

Se você estiver interessado em APIs de banco de dados assíncronas para Java, saiba que existe uma nova iniciativa para criar um conjunto de APIs padrão baseadas em CompletableFuture e lambdas. Também há uma implementação dessas APIs sobre JDBC que pode ser usada para praticar essas APIs: https://github.com/oracle/oracle-db-examples/tree/master/java/AoJ O JavaDoc é mencionado no README do o projeto github.

Jean de Lavarene
fonte