问题

我想知道是否有办法对数据库进行异步调用?

例如,假设我有一个需要很长时间才能处理的大请求,我想发送请求并在请求返回值时接收通知(通过传递监听器/回调等)。我不想阻止等待数据库回答。

我不认为使用线程池是一种解决方案,因为它不能扩展,在繁重的并发请求的情况下,这将产生大量的线程。

我们正面临着网络服务器的这种问题,我们通过使用select / poll / epoll系统调用找到了解决方案,以避免每个连接有一个线程。我只是想知道如何在数据库请求中使用类似的功能?

注意:我知道使用FixedThreadPool可能是一个很好的解决办法,但我很惊讶没有人开发出一个真正异步的系统(没有使用额外的线程)。

更新
由于缺乏真正实用的解决方案,我决定自己创建一个库(finagle的一部分):finagle-mysql。它基本上解码/解码mysql请求/响应,并在引擎盖下使用Finagle / Netty。即使有大量的连接,它也能很好地扩展。


#1 热门回答(138 赞)

我不明白在Actors,执行器或其他任何东西中包含JDBC调用的任何提议方法如何在这里有所帮助 - 有人可以澄清。

当然,基本问题是JDBC操作阻塞了套接字IO。当它这样做时,它会阻止线程运行 - 故事结束。无论你选择使用什么包装框架,最终都会有一个线程在每个并发请求中保持繁忙/阻塞状态。

如果底层数据库驱动程序(MySql?)提供了拦截套接字创建的方法(请参阅SocketFactory),那么我想可以在JDBC api之上构建一个异步事件驱动的数据库层,但我们必须封装整个JDBC在事件驱动的外观后面,并且该外观看起来不像JDBC(在事件驱动之后)。数据库处理将在与调用者不同的线程上发生异步,并且你必须弄清楚如何构建不依赖于线程关联的事务管理器。

像我提到的方法之类的东西甚至允许单个后台线程处理并发JDBC exec的负载。实际上,你可能会运行一个线程池来使用多个核心。

(当然,我没有评论原始问题的逻辑,只是意味着在没有选择器模式的用户的情况下可以在具有阻塞套接字IO的情况下实现并发的响应 - 更简单地只是为了计算典型的JDBC并发性并放置在正确大小的连接池中)。

看起来MySql可能会按照我建议的方式做一些事情--- http://code.google.com/p/async-mysql-connector/wiki/UsageExample


#2 热门回答(37 赞)

通过JDBC对数据库进行异步调用是不可能的,但是你可以使异步调用JDBC与Actors(例如,actor通过JDBC调用DB,并在调用结束时向第三方发送消息),或者,如果你喜欢CPS,withpipelined futures(promises)(一个很好的实现是ScalazPromises)

我不认为使用线程池是一种解决方案,因为它不能扩展,在繁重的并发请求的情况下,这将产生大量的线程。

默认情况下,Scala actor是基于事件的(不是基于线程的) - 延续调度允许在标准JVM设置上创建数百万个actor。

如果你的目标是Java,Akka Framework是一个Actor模型实现,它具有适用于Java和Scala的良好API。

除此之外,JDBC的同步特性对我来说非常有意义。数据库会话的成本远远高于被阻止的Java线程(在前端或后台)和等待响应的成本。如果你的查询运行的时间太长以至于执行程序服务(或包装Actor / fork-join / promise并发框架)的功能对你来说是不够的(并且你消耗了太多线程),那么首先应该考虑一下数据库加载。通常,数据库的响应速度非常快,并且使用固定线程池支持的执行程序服务是一个足够好的解决方案。如果你有太多长时间运行的查询,你应该考虑提前(预)处理 - 比如每晚重新计算数据或类似的东西。


#3 热门回答(11 赞)

也许你可以使用JMS异步消息系统,它可以很好地扩展,恕我直言:

  • 向队列发送消息,订阅者将接受该消息,并运行SQL进程。你的主进程将继续运行并接受或发送新请求。
  • 当SQL进程结束时,你可以以相反的方式运行:使用进程的结果向ResponseQueue发送消息,并且客户端上的侦听器接受它并执行回调代码。

原文链接