使用多个ConnectionPool的正确方法

时间:2018-04-06 12:22:33

标签: scala jdbc scalikejdbc

在我的应用程序中,我必须逐个与多个MySQL DB进行交互(只读)。对于每个DB,我需要一定的连接数。在单一伸展中不会发生与DB的交互:我查询数据库,花一些时间处理结果,再次查询数据库,再次处理结果等等。

这些互动中的每一个都需要多个连接 [我同时触发多个查询],因此当我开始与之交互时,我需要一个ConnectionPool生成数据库一直存在,直到我完成了对该数据库的所有查询(包括我不查询的临时时间间隔,只处理结果)。

我能够成功创建一个具有所需连接数的ConnectionPool,并获得implicit session,如下所示

def createConnectionPool(poolSize: Int): DBSession = {
 implicit val session: AutoSession.type = AutoSession

 ConnectionPool.singleton(
   url = "myUrl",
   user = "myUser",
   password = "***",
   settings = ConnectionPoolSettings(initialSize = poolSize)
 )

 session
}

然后我在需要与DB交互的方法中传递此implicit session。通过这种方式,我可以使用此poolSize同时触发session没有查询 。很公平。

def methodThatCallsAnotherMethod(implicit session: DBSession): Unit = {
  ...
  methodThatInteractsWithDb
  ...
}

def methodThatInteractsWithDb(implicit session: DBSession): Unit = {
  ...
  getResultsParallely(poolSize = 32, fetchSize = 2000000)
  ...
}

def getResultsParallely(poolSize: Int, fetchSize: Int)(implicit session: DBSession): Seq[ResultClass] = {
  import java.util.concurrent.Executors
  import scala.concurrent.ExecutionContext
  import scala.concurrent.duration._

  implicit val ec: ExecutionContext = ExecutionContext.fromExecutorService(Executors.newFixedThreadPool(poolSize))

  val resultsSequenceFuture: Seq[Future[ResultClass]] = {
    (0 until poolSize).map { i =>
      val limit: Long = fetchSize
      val offset: Long = i * fetchSize

      Future(methodThatMakesSingleQuery(limit, offset))
    }
  }
  val resultsFutureSequence: Future[Seq[ResultClass]] = Future.sequence(resultsSequenceFuture)

  Await.result(resultsFuture, 2.minutes)
}

此技术存在两个问题:

  1. 我的应用程序非常庞大,并且有许多嵌套方法调用,因此将implicit session传递给所有这样的方法(见下文)是不可行的。
  2. 除了上述与不同数据库的逐个交互之外,在整个应用程序的整个生命周期中,我还需要单一连接到另一个(固定)数据库。每隔几分钟,此连接将用于进行小写操作 n(记录与其他DB交互的进度)。因此,我需要多个ConnectionPool s,每个数据库一个
  3. 根据我ScalikeJdbc docs所做的,我提出了以下方式,并不要求我通过implicit session到处都是。

    def createConnectionPool(poolName: String, poolSize: Int): Unit = {
      ConnectionPool.add(
        name = poolName,
        url = "myUrl",
        user = "myUser",
        password = "***",
        settings = ConnectionPoolSettings(initialSize = poolSize)
      )
    }
    
    def methodThatInteractsWithDb(poolName: String): Unit = {
      ...
      (DB(ConnectionPool.get(poolName).borrow())).readOnly { implicit session: DBSession =>
        // interact with DB
        ...
      }
      ...
    }
    

    虽然这有效,但我不再能够并行化数据库交互。这种行为很明显,因为我使用borrow()方法,从获取单一连接。反过来,这让我想知道为什么AutoSession事先有效:为什么我能够使用单个implicit session同时触发多个查询?如果那件事有效,那么为什么这不起作用呢?但我没有找到如何从支持多个连接的DBSession获取ConnectionPool的示例。

    总而言之,我有2个问题和2个解决方案:每个问题一个。但我需要一个解决这两个问题的单一(通用)解决方案。

    ScalikeJdbc的有限文档并未提供大量帮助,ScalikeJdbc上的博客/文章几乎不存在。 请建议正确的方法/一些解决方法。

    框架版本

    • Scala 2.11.11
    • "org.scalikejdbc" %% "scalikejdbc" % "3.2.0"

1 个答案:

答案 0 :(得分:1)

由于@Dennis Hunziker,我得以找出the correct way来释放从ScalikeJdbc的{​​{1}}借来的连接。可以完成以下操作:

ConnectionPool

有了这个,现在我可以并行化与池的交互了。


为了解决管理多个import scalikejdbc.{ConnectionPool, using} import java.sql.Connection using(ConnectionPool.get("poolName").borrow()) { (connection: Connection) => // use connection (only once) here } // connection automatically returned to pool 并在多个ConnectionPool之间使用连接的问题,我最终编写了一个class,可以找到完整的代码{{3} }。通过卸载

的任务
  • 创建池
  • 从池中借用连接
  • 删除游泳池

对于一个ConnectionPoolManager对象,我可以在项目的任何地方使用它,我可以清除很多混乱情况,并消除了跨方法链的singleton需求传递。


EDIT-1

虽然我已经hereimplicit session的完整代码,但这里有一个快速提示,提示您如何进行操作

以下ConnectionPoolManager的跟踪方法可让您从ConnectionPoolManager s借用连接

ConnectionPool

此后,在整个代码中,您可以使用上述方法从池中借用连接并进行查询

def getDB(dbName: String, poolNameOpt: Option[String] = None): DB = {
  // create a pool for db (only) if it doesn't exist
  addPool(dbName, poolNameOpt)

  val poolName: String = poolNameOpt.getOrElse(dbName)
  DB(ConnectionPool.get(poolName).borrow())
}