2011-11-02
大量のインサートを高速化する / DBIx::Customリファレンス
DBIx::Customは便利なDBIのラッパーですが、大量のinsertなどを連続して行う場合の性能に関しては、生のDBIに比べて性能がかなり劣化します。
そのような場合は複数のデータを一度に挿入しましょう。DBIx::Customのinsertメソッドには、複数のデータを一度に渡すことができます。
my @params = [{id => 1, title => 'Perl'}, {id => 2, title => 'Ruby'}]; $dbi->insert(\@params, table => 'book');
大量のinsertを行うときのパフォーマンスの劣化は、DBIと比較して小さな劣化に収まります。
idオプションが使用できないといことが、複数insertにおける制限です。またフィルタ、タイプルール、バインドタイプのオプションのすべてがない場合には、insertはさらに高速化されます。タイプルールがすでに設定されている場合は、type_rule_offに1に設定することで、タイプルールを無効にすることができます。
パラメーターの最初のハッシュの値によって、生成されるSQLが決定するということに注意してください。つまり、残りのパラメータには、最初のパラメータと同じ列が含まれている必要があるということに、注意してください。
ベンチマークスクリプトとベンチマークの結果です。データベースにはSQLiteを使用しました。
use strict; use warnings; use Benchmark qw/timethese cmpthese/; use DBIx::Custom; my $dbi = DBIx::Custom->connect( dsn => 'dbi:SQLite:dbname=:memory:', ); $dbi->execute("create table book (id, title)"); # 性能の比較 my $result = timethese(100, { # 通常 normal => sub { for my $i (1 .. 100) { my $param = {id => $i, title => 'Perl'}; $dbi->insert($param, table => 'book'); } }, # 複数まとめてインサート multiple => sub { my @params; for my $i (1 .. 100) { push @params, {id => $i, title => 'Perl'}; } $dbi->insert(\@params, table => 'book'); }, # 生のDBIでステートメントハンドルを再利用 raw => sub { my $sth; for my $i (1 .. 100) { $sth ||= $dbi->dbh->prepare('insert into book (id, title) values (?, ?)'); my $id = $i; my $title = 'Perl'; $sth->execute($i, $title); } } }); cmpthese($result);
ベンチマークの結果は、生のDBIを利用するものよりも、若干パフォーマンスが落ちる程度で収まっています。通常のinsertを使う場合に比較して、10倍近い差があるので、大量のインサートを同時に行う場合に、利用するとよいと思います。
Rate normal multiple raw
normal 22.3/s -- -90% -92%
multiple 213/s 855% -- -23%
raw 278/s 1147% 31% --
バルクインサートで高速化
データベースがバルクインサートをサポートしている場合は、バルクインサートを行うことができます。現在のところMySQLとPostgreSQLで利用することができます。データベースの機能を使って、複数のデータを一度に挿入できるので、さらなる高速化が期待できます。
$dbi->insert($params, table => 'book', bulk_insert => 1);
MySQLでのbulk_insertでのベンチマーク
以下はベンチマークスクリプトです。
use strict; use warnings; use Benchmark qw/timethese cmpthese/; use DBIx::Custom; my $dbi = DBIx::Custom->connect(dsn => 'dbi:mysql:database=usertest', user => 'root', connector => 1); # 性能の比較 my $result = timethese(100, { normal => sub { $dbi->connector->txn(sub { $dbi->delete_all(table => 'book'); for my $i (1 .. 100) { my $param = {id => $i, title => 'Perl'}; $dbi->insert($param, table => 'book'); } }); }, multiple => sub { $dbi->connector->txn(sub { $dbi->delete_all(table => 'book'); my @params; for my $i (1 .. 100) { push @params, {id => $i, title => 'Perl'}; } $dbi->insert(\@params, table => 'book'); }); }, bulk_insert => sub { $dbi->connector->txn(sub { $dbi->delete_all(table => 'book'); my @params; for my $i (1 .. 100) { push @params, {id => $i, title => 'Perl'}; } $dbi->insert(\@params, table => 'book', bulk_insert => 1); }); }, raw => sub { $dbi->connector->txn(sub { $dbi->delete_all(table => 'book'); my $sth; for my $i (1 .. 100) { $sth ||= $dbi->dbh->prepare('insert into book (id, title) values (?, ?)'); my $id = $i; my $title = 'Perl'; $sth->execute($i, $title); } }); } }); cmpthese($result);
ベンチマークの結果で、bulk_insertを使うと、通常のinsertの通常の5倍くらいのパフォーマンスがでるようです。
Rate normal multiple raw bulk_insert
normal 24.0/s -- -72% -76% -83%
multiple 86.2/s 259% -- -16% -40%
raw 102/s 326% 18% -- -29%
bulk_insert 143/s 496% 66% 40% --
もしバルクインサートでパフォーマンスがでないときは、データベースのinsertのためのメモリ領域に不足がないかを一度確認してください。このメモリ領域が足りない場合は、スワップイン・スワップアウトが発生して、とても遅くなります。
- 24 https://www.google.co.jp/
- 3 http://www.google.co.jp/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&cad=rja&ved=0CC0QFjAA&url=http://d.hatena.ne.jp/perlcodesample/20111102&ei=UsgqUc6IJYmHmQWgnoDoBw&usg=AFQjCNE9zeQEUGMBkW0MDM-EjCEMqjpMQw&bvm=bv.42768644,d.dGY
- 3 http://www.google.co.jp/url?sa=t&rct=j&q=&esrc=s&source=web&cd=3&ved=0CDkQFjAC&url=http://d.hatena.ne.jp/perlcodesample/20111102/1322478136&ei=1ZpIUeqnMeWuiQe2l4DAAg&usg=AFQjCNF5TzfQoHvyn8oV2kBsIPOgmdNAWA&sig2=bTXV0ManhLEm0t3N9xKH_g&bvm=bv.438
- 2 http://search.yahoo.co.jp/search?p=perl+innsert+ループ 変数&aq=-1&oq=&ei=UTF-8&fr=mozff&x=wrt
- 2 http://www.google.co.jp/url?sa=t&rct=j&q=&esrc=s&frm=1&source=web&cd=3&cad=rja&ved=0CD0QFjAC&url=http://d.hatena.ne.jp/perlcodesample/20111102/1322478136&ei=TaRvUeW1FYa0kAX2yoDgAg&usg=AFQjCNF5TzfQoHvyn8oV2kBsIPOgmdNAWA&sig2=IYn-DCRVrc71EQYVgzW
- 2 http://www.google.co.jp/url?sa=t&rct=j&q=&esrc=s&source=web&cd=4&ved=0CEcQFjAD&url=http://d.hatena.ne.jp/perlcodesample/20111102/1322478136&ei=nehcUdmbEIqtiQfeq4CgCw&usg=AFQjCNF5TzfQoHvyn8oV2kBsIPOgmdNAWA&sig2=y72pspF3R2h6N0f5108AMA&bvm=bv.447
- 2 http://www.google.co.jp/url?sa=t&rct=j&q=find::bin&source=web&cd=1&ved=0CCoQFjAA&url=http://d.hatena.ne.jp/perlcodesample/20080422/1208964445&ei=wmrTTs_UIJDUmAW3--3tCg&usg=AFQjCNGIqCMtxGREtzRAULDFBF5Wo2500A&sig2=rv6ERN1vkThBkKCCHE31nw
- 2 http://www.google.co.jp/url?sa=t&rct=j&q=mery+perl+マクロ&source=web&cd=6&ved=0CEkQFjAF&url=http://d.hatena.ne.jp/perlcodesample/20100503/1270894115&ei=AGrTTtxVh_eYBdKq2LUD&usg=AFQjCNGAkfnkaXG_W8p7Qymrca2LK_jsTg
- 2 http://www.google.co.jp/url?sa=t&rct=j&q=perl 例外発生時にエラー画面&source=web&cd=1&ved=0CB0QFjAA&url=http://d.hatena.ne.jp/perlcodesample/20090302/1235211715&ei=1
- 2 http://www.google.co.jp/url?sa=t&rct=j&q=perl 改行 削除&source=web&cd=2&sqi=2&ved=0CC8QFjAB&url=http://d.hatena.ne.jp/perlcodesample/20080226/1204032972&ei=rW3TTpiBB8jymAXx7uClDQ&usg=AFQjCNEY5yUeHpq0raXTg5HZ_dM1