DB投入が遅い…

3000件くらいのinsertだと1分もあれば終わるのに、1万件だと30分とか1時間とか2時間…。ひどいときは5時間とか。計算合わないよね。何故?ググったりして色々調べる。すると、インデックス張ってあるとinsertの度にいちいちインデックス張り直すから、遅いなんてのが目についた。やってみると確かに速い。つまり、insertの前にインデックスを一度切って、insertをバーっとやって、終わったらインデックス張り直す。なるほど、確かに速い。けどインデックス張り直すのは、やっぱり遅いね。
他にも、テーブルをロックしてからinsertして、終わったらアンロックするとか、いちいちコミットしないで、まとめてコミットするとか、色々と実験。しかし、大して速くならない。実験の結果、現状30分のものが27分とか26分とか、そんなもん。ロックまたはコミットするinsert件数を500件とか1000件とか変えてみたが、結果に変化なし。何か決定打じゃあないねぇ。
で、データ量と投入時間の関係とかDBに格納されたデータ量とか、各種数値とにらめっこすることしばし。ようやく気がついてしまった。既にデータがいっぱい入っている状態からのデータ投入は遅い。空のテーブルに入れると速い。実はデータ投入の際に「同じ」データは投入しないようにチェックしていたのだが、これが遅かった。最初のうちはDBが空だから良いのだが、DBに投入されたデータが100万件とか溜まってくると、遅くなる。DB中のどれかと「同じ」かどうかいちいちクエリ発行してチェックしてからinsertだから、データ量の2乗で遅いのな。バカだねぇ、こんなことくらいすぐ気づけよ。
どうしよう?常識的には「同じ」と判定するためのデータにインデックスを張れば、NlogNになるから劇的に速くなるよね。そうしようか?で、考えることしばし。もっと良い方法があった。実は同じデータというのは「これから入れようとしているデータ」の中にしかない、という性質がありそう。そこで、DBにこれから入れようとしているデータに、もうDBに入っているものがあるか?をチェックするのではなく、入れる前にこれから入れるもの同士で同じものをチェックして、違うものだけを入れる、という風に変えてみることにした。これで既にDBに入っているデータ量とは無関係な処理になるので、速く…、なるはずだよなぁ。