从数据库加载大量记录时内存不足

时间:2019-02-19 12:06:22

标签: scala akka slick akka-stream reactive-streams

我在Akka Streams中使用平滑功能从数据库(postgresql)加载大量记录(〜2M)并将它们写入S3文件。但是,我注意到下面的代码适用于约50k的记录,但是对于超过100k的记录无效。

  val allResults: Future[Seq[MyEntityImpl]] =
    MyRepository.getAllRecordss()

  val results: Future[MultipartUploadResult] = Source
    .fromFuture(allResults)
    .map(seek => seek.toList)
    .mapConcat(identity)
    .map(myEntity => myEntity.toPSV + "\n")
    .map(s => ByteString(s))
    .runWith(s3Sink)

下面是myEntity外观的示例:

case class MyEntityImpl(partOne: MyPartOne, partTwo: MyPartTwo) {
  def toPSV: String = myPartOne.toPSV + myPartTwo.toPSV
}
case class MyPartOne(field1: String, field2: String) {
  def toPSV: String = {s"$field1|"+s"$field2"}
}
case class MyPartOne(field1: String, field2: String) {
  def toPSV: String = {s"$field1|"+s"$field2"}
}

我正在寻找一种以更被动的方式执行此操作的方法,以确保它不会耗尽内存。

1 个答案:

答案 0 :(得分:1)

基本问题

问题在于,您正在将所有记录从数据库中拉到本地内存中,然后再将它们分派到s3Sink

您可能会在MyRepository.getAllRecords()方法中将数据首先拉到内存中。大多数Seq实现(如果不是全部的话)都是基于内存的。您肯定要利用本地内存的第二个位置是seek.toList,因为List会将所有数据存储在内存中。

解决方案

而不是从Seq you should be returning a slick-based akka Source directly返回getAllRecords。这将确保您的物化流在进入s3之前仅需要用于临时处理步骤的内存。

如果您的方法定义更改为:

def getAllRecords() : Source[MyEntityImpl, _]

然后其余的流将以反应方式运行:

MyRepository
  .getAllRecords()
  .map(myEntity => myEntity.toPSV + "\n")
  .map(ByteString.apply)
  .runWith(s3Sink)