読者です 読者をやめる 読者になる 読者になる

あざらし備忘録。

渋谷ではたらく音ゲー大好きウェッブエンジニアがいろいろ思った事やった事を書いていくブログです

Doctrineのバルクインサートはマルチインサートではない[Doctrine][Symfony2][ORM]

今回はDoctrineの注意点的な所の備忘録として。

Doctrine 2 Batch Processing — Doctrine Project

doctrineのバルクインサートはinsert文を1つにまとめられるわけではない

僕はバルクインサートといえば次のようなSQLを想像していました。

insert into (...) values (...), (...), (...), ...

あるテーブルに対して複数行インサートをしたいときに、insert文を並べなくてもインサートする値を並べていけばその個数分インサートするというやつです。

普通のインサートを並べるよりも非常に高速かつ問い合わせるのが1回で済むのでスマートです。

ってきりDoctrineでもこの動きができるんだーと簡単に思ってしまったのですが、よく読むとDoctrineの提供するbulk insertはこのような挙動ではないようなのです。

Doctrine曰く

Some people seem to be wondering why Doctrine does not use multi-inserts (insert into (...) values (...), (...), (...), ...

First of all, this syntax is only supported on mysql and newer postgresql versions. Secondly, there is no easy way to get hold of all the generated identifiers in such a multi-insert when using AUTO_INCREMENT or SERIAL and an ORM needs the identifiers for identity management of the objects. Lastly, insert performance is rarely the bottleneck of an ORM. Normal inserts are more than fast enough for most situations and if you really want to do fast bulk inserts, then a multi-insert is not the best way anyway, i.e. Postgres COPY or Mysql LOAD DATA INFILE are several orders of magnitude faster.

These are the reasons why it is not worth the effort to implement an abstraction that performs multi-inserts on mysql and postgresql in an ORM.

I hope that clears up some questionmarks.

僕が想像していたバルクインサートはマルチインサートという言葉で表現されていて、Doctrineはそうではないよと書かれています。

このマルチインサート記法はmysqlと新しいpostgresqlでしかサポートされていないから、その一歩抽象化レイヤーであるDoctrine ORMとしてはその形をサポートはしないと。

ORMとしては普通のinsertがボトルネックになることはまぁ稀(使い方が違う)で、どうしても早さを求めるinsertをしたいならマルチインサートよりもより高速なファイルロードによるインサートLOAD DATA INFILEをしましょうよと。

こんなような旨の記述がされています。

感想

確かにORMだしねっていう着地になるのですが、あまりじっくり読んでなかった僕は「おぉ、よく使うもんだからORMにあのバルクインサート機能つけてカジュアルにできるようになったのかー!責務はちょっと超えてる感あるけど便利さとトレードオフしたらありかも :)」と思ってしまいましたw

だから上で貼ったドキュメントの例でもbulk sizeが20なんていう小さいサイズのインサートサイクルだったのですね。bulk sizeを普通のマルチインサートっぽく大きくするとその分のinsertが走ってDBへのinsert攻撃みたいになってしまうのでw

バッチで即効性を求められることもないと思う(ないようにバッチを回すべき)ので、メモリ効率と速さのバランスを考えた時にぬるぬると時間はまぁ気にせず鈍行運転する、みたいなのをこのDoctrineの提供するbulk insert機能で実装したら良いのではないかなーと思いました。

どうしてもマルチインサートがしたければDBALレイヤーを使って直に書いてしまえばOKと思います:)

という自分的には腹落ちするところでおちついたので良かったです。

まとめ

  • Doctrineのbulk insertはよく見るマルチインサートではない
  • マルチインサートがしたければDBALを用いてSQLを書く
  • 大規模データを高速に処理したければマルチインサートではなくファイルでのインサートを行う

おわりに

もし僕のように俗に言うマルチインサートの感覚で使っていた方はご留意を! 大規模データの時に想像以上に時間がかかったり想像以上に負荷がかかったりしてしまうかもしれません。

解釈の間違ってる所とかあったら教えてくださるととても嬉しいですmm