Slickで取ったSQLの結果はたいていlistかfirstOptionで取っていたのだけど、大きめの結果を取る場合はfetchした方が良いよなという事で、elementsを使ってみる。
確認のため、1000万件の結果を取得させてみて、listと比べてメモリ消費量が少ないことを確認する。
とりあえずテーブル作成。
import scala.slick.driver.H2Driver.simple._
case class Coffee(id: Int, name: String, price: Int, size: String, sugar: Boolean)
object Coffees extends Table[Coffee]("coffees") {
def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
def name = column[String]("name", O.NotNull)
def price = column[Int]("price", O.NotNull)
def size = column[String]("size", O.NotNull)
def suger = column[Boolean]("sugar", O.Default(true))
def * = id ~ name ~ price ~ size ~ sugar <> (Coffee, Coffee.unapply _)
}
1000万件データを入れる。
val url = "jdbc:h2:test1"
Database.forURL(url, driver = "org.h2.Driver") withSession { implicit session: Session =>
Coffees.ddl.create
val rand = new Random()
val size = Array("L", "S", "M")
for (i <- 0 to 10000000) {
Coffees.insert(Coffee(i, "適当", rand.nextInt(1000), size(rand.nextInt(3)), true))
}
}
全件取ってきて、priceのMAXを取るという無駄な処理を書く。listではなくelementsで取る。2ヶ所で簡単なメモリ量計測を入れる。
val url = "jdbc:h2:test1"
Database.forURL(url, driver = "org.h2.Driver") withSession { implicit session: Session =>
def mem() = (Runtime.getRuntime.totalMemory - Runtime.getRuntime.freeMemory) / 1024 / 1024
val startMem = mem()
val ite = Query(Coffees).elements
println(mem() - startMem)
try ite.foldLeft(0) { (i, c) => c.price.max(i) }
finally ite.close()
println(mem() - startMem)
}
この時、メモリの使用料はどちらも158MB→230MB。意外と使いますね。
次にlistで取った場合。コードの差で違いが出ないように、ほぼ同じようにコードにする。
val url = "jdbc:h2:test1"
Database.forURL(url, driver = "org.h2.Driver") withSession { implicit session: Session =>
def mem() = (Runtime.getRuntime.totalMemory - Runtime.getRuntime.freeMemory) / 1024 / 1024
val startMem = mem()
val ite = Query(Coffees).list.iterator
println(mem() - startMem)
try ite.foldLeft(0) { (i, c) => c.price.max(i) }
//finally ite.close()
println(mem() - startMem)
}
この時、メモリの使用量は1084MB→1239MB。
1239MB → 230MBということで、ちゃんと仕事はしてくれているようだ。