我试图在Slick的帮助下快速将一些100M记录插入到MySQL表中。
天真地,我预计,如果我将测试数据集提供为Stream
,那么Slick将使用它而不是贪婪:
val testData = Stream.continually(
UUIDRecord(uuid = UUID.randomUUID().toString, value = (Math.random()*100).toLong)
).take(100000000)
val batchInsert:DBIO[Option[Int]] = records ++= testData
val insertResult = db.run(batchInsert)
但是我觉得我算错了,Slick在将它传递给MySQL之前实现了该流的实现,因为我在运行时遇到了这个错误:
#
Java HotSpot(TM) 64-Bit Server VM warning: INFO: os::commit_memory(0x00000000b9700000, 281542656, 0) failed; error='Cannot allocate memory' (errno=12)
# There is insufficient memory for the Java Runtime Environment to continue.
# Native memory allocation (mmap) failed to map 281542656 bytes for committing reserved memory.
# An error report file with more information is saved as:
# /ssd2/projects/StreamingDB/hs_err_pid28154.log
Process finished with exit code 1
你可以建议吗?我知道Slick可以在流模式下运行查询(即它是一个反应流发布者),但有没有办法在"流媒体"中插入大量的记录。方式是什么?
答案 0 :(得分:1)
首先,您可能对此GitHub issue感兴趣。简而言之,批处理模式需要JDBC驱动程序的支持。
即使假设为您启用了批处理模式,它仍然很可能无法正常工作。不幸的是,你没有为你的OOM提供实际的堆栈跟踪,但我敢打赌它位于MultiInsertAction.run
内,更具体地说是在st.addBatch()
调用内st
是java.sql.PreparedStatement
的子类。问题是即使在批处理模式下,也必须首先累积批次。换句话说,客户端应该累积将作为INSERT
语句的一部分传递的所有数据,这需要以某种形式实际实现它。所以重点是,即使Slick没有实现流,JDBC也会。
我能想到的唯一解决方法是将您的数据流明确地分成几个批次并插入那些较小的批次。你可以考虑这样的事情:
val testData = Stream.continually(
UUIDRecord(uuid = UUID.randomUUID().toString, value = (Math.random()*100).toLong)
).take(100000000)
val BATCH_SIZE = 1000
val futures = testData.grouped(BATCH_SIZE).map(batch => {
val batchInsert: DBIO[Option[Int]] = records ++= batch
db.run(batchInsert)
})
val all: Future[Int] = Future.sequence(futures).map(it => it.map(_.getOrElse(0)).sum)