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ということで、ちゃんと仕事はしてくれているようだ。