1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Kotlin】ExposedでPostgreSQLにバルクインサート

Posted at

batchInsertを使っているのに遅い気がする

 気がするというかかなり遅かった。
 たかだか10000件のInsertが10秒だって?

 うわっ…私のDB、遅すぎ…?

 というわけで少しこの問題を調べてみた。

確認:PostgreSQLのreWriteBatchedInsertsをTrueにしているか?

    Database.connect(PGSimpleDataSource().apply {
        setURL("jdbc:postgresql://localhost:5432/postgres")
        user = "pguser"
        password = "pgpass"
        reWriteBatchedInserts = true  // ← この野郎をTrueにしまくる
    })

ドライバとExposedのバージョンを確認する

postgresqlのバージョンは**>=42.2.2**であればinsert on conflictの不具合が解消されているという事で、まあ最新ならよろし。

問題はexposedのバージョンで、下記のようにexposed-coreではなく、exposedの最新implementation 'org.jetbrains.exposed:exposed:0.17.13'を使っていた事に問題があった。よってこれを現在最新の以下のバージョンに書き換えた。

dependencies {
    implementation 'org.postgresql:postgresql:42.2.23'

    implementation 'org.jetbrains.exposed:exposed-core:0.32.1'
    runtimeOnly 'org.jetbrains.exposed:exposed-jdbc:0.32.1'
}

結論をいうと10倍以上早くなった……が

reWriteBatchedInsertsをfalseにしてもさほど結果が変わらないという謎が実は残っている。あれ? 結局バージョンを上げただけなのって思うかも知れないが、そんだけの事で2時間ぐらいハマってしまった。

ソース上の変更点

バージョンを上げるとbatchInsertが例外を吐くようになった。
プライマリキーのUUIDのデフォルトをuuid_generate_v4()としているが、デフォルトは使えなくなっている。全ての列に値を設定してやると良いようだ。

object HorseRaceResultTable: Table() { 
  val id = uuid("id")
  val raceName = text("レース名")
  val winner = text("勝ち馬")
  val reserve = text("reserve")
}

fun main() {
  Database.connect(PGSimpleDataSource().apply {
    setURL("jdbc:postgresql://localhost:5432/postgres")
    user = "pguser"
    password = "pgpass"
    reWriteBatchedInserts = true
  })

  val values = mutableListOf<Pair<String,String>>()
  
  values.add("宝塚記念" to "クロノジェネシス")
  values.add("安田記念" to "ダノンキングリー")
  values.add("東京優駿" to "シャフリヤール")
  
  measureTimeMillis {  // ← こいつは計測のため
    transaction {
      HorseRaceResultTable.batchInsert(values) { // ← batchInsertを使う
        this[HorseRaceResultTable.id]       = UUID.randomUUID() // ← UUIDも自分で設定
        this[HorseRaceResultTable.raceName] = it.first
        this[HorseRaceResultTable.winner]   = it.second
        this[HorseRaceResultTable.reserve]  = "" // ← 使わなくてもちゃんと設定
      }
    }
  }.let { println("insert time is $it ms") }
}

MySQLだったらrewriteBatchedStatements=trueでサクッと行くらしい

でもPostgreSQLはハマってる人結構たくさんいたんだよね。速くなったから使えてるんだと思うんだけど。

あと意外と重要なのがログレベルをDEBGUGにしてたらExposedがSQL全出力しちゃうから大量データのInsertではここも気をつけましょうネ。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?