あるテーブルに対して複数のレコードを同時に登録したいことがあります。
このときに登録する数だけINSERTを発行するのはあまり効率がよくありません。
そこでBULK INSERTと言われる単発のSQLで一括登録する方法がよく使われます。
BULK INSERTとは
単純なbooksというテーブルを使い、BULK INSERTはどういうものか確認する。
CREATE TABLE `books` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`created_at` datetime NOT NULL,
`updated_at` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
複数のINSERTを発行する。
INSERT INTO books (columns) VALUES (values);
INSERT INTO books (columns) VALUES (values);
INSERT INTO books (columns) VALUES (values);
BULK INSERTで一括登録する。
INSERT INTO books (columns) VALUES (values), (values), (values);
それぞれのSQLでこのような違いがありますが、登録されるレコード結果は同じになります。
RailsでBULK INSERTを使う
RailsのActiveRecordでBULK INSERTを使うには、activerecord-importという大変便利なgemがあるので利用させていただきます。
activerecord-import
使い方はとても簡単で、登録するレコード(model)のインスタンスをすべて配列にいれて、クラスメソッドのimportにそれを渡すだけです。
books = []
10.times do |i|
books << Book.new(:name => "book #{i}")
end
Book.import books
これで10回分のINSERT文が1つにまとめられます。
パフォーマンスを検証する
1度に登録する数を100レコードにし、それを100回繰り返す処理で計測してみる。
Railsの通常処理でINSERTを繰り返すパターン。
100.times do |i|
100.times do |_i|
Book.create! :name => "book_#{i}_#{_i}"
end
end
BEGIN;
INSERT INTO books (...) VALUES (...);
COMMIT;
BEGIN;
INSERT INTO books (...) VALUES (...);
COMMIT;
BEGIN;
INSERT INTO books (...) VALUES (...);
COMMIT;
...
activerecord-importを使って、BULK INSERTするパターン。
100.times do |i|
books = []
100.times do |_i|
books << Book.new(:name => "book_#{i}_#{_i}")
end
Book.import books
end
INSERT INTO `books` (...) VALUES (...), (...), (...), ...;
Railsの通常パターンでは毎回COMMITされてしまうので、TRANSACTIONを使ったパターンも追加する。
100.times do |i|
ActiveRecord::Base.transaction do
100.times do |_i|
Book.create! :name => "book_#{i}_#{_i}"
end
end
end
BEGIN
INSERT INTO books (...) VALUES (...);
INSERT INTO books (...) VALUES (...);
INSERT INTO books (...) VALUES (...);
...
COMMIT
計測結果
Insert | BulkInsert | Transaction |
---|---|---|
10.6876sec | 4.0554sec | 7.1316sec |
それぞれ7回計測し、1番速いものと遅いものを除外した5回平均のデータ。
実行環境 MacBookPro, Core i7 2.9GHz, SSD256GB, Mem8GB
単純なテーブルでの比較ですが、BULK INSERTを使うとパフォーマンスは大きく向上しました。
複数レコードを登録するときは、ぜひBULK INSERTで処理しましょう!