2013-03-14 15 views
10

編集:私のデバッグとロギングのいくつかに基づいて、DELETE FROM table WHERE id = xはなぜよりもはるかに高速であるのかという質問があると思います。xは単なるIDです。Hibernateのバッチ削除と単一の削除

私は最近、バッチ削除と各行の削除を1つずつテストし、バッチ削除がはるかに遅いことに気付きました。テーブルには削除、更新、挿入のトリガがありましたが、トリガの有無にかかわらずテストしました。毎回バッチ削除が遅くなりました。 これはなぜ、私はこれをデバッグすることができますか?私が理解しているように、私は実際にトリガーが起動する回数を減らすことはできませんが、私はもともと「削除」クエリの数を減らすことがパフォーマンスに役立つと考えていました。

私は以下の情報をいくつか掲載しました。関連するものは除外していますか?

削除は万のバッチなどのコードを見て何かに行われています。

private void batchDeletion(Collection<Long> ids) { 
    StringBuilder sb = new StringBuilder(); 
    sb.append("DELETE FROM ObjImpl WHERE id IN (:ids)"); 

    Query sql = getSession().createQuery(sb.toString()); 
    sql.setParameterList("ids", ids); 

    sql.executeUpdate(); 
} 

1つだけの行を削除するためのコードは、基本的に次のとおりです。

SessionFactory.getCurrentSession().delete(obj); 

の表は、2つのインデックスを持っています削除のいずれにも使用されません。カスケード操作は行われません。

Delete on table (cost=12.82..24.68 rows=3 width=6) (actual time=0.143..0.143 rows=0 loops=1) 
    -> Bitmap Heap Scan on table (cost=12.82..24.68 rows=3 width=6) (actual time=0.138..0.138 rows=0 loops=1) 
     Recheck Cond: (id = ANY ('{1,2,3}'::bigint[])) 
     -> Bitmap Index Scan on pk_table (cost=0.00..12.82 rows=3 width=0) (actual time=0.114..0.114 rows=0 loops=1) 
       Index Cond: (id = ANY ('{1,2,3}'::bigint[])) 
Total runtime: 3.926 ms 

私は真空にし、私はテストのために自分のデータをリロードし、私のテストデータは386660行が含まれているたびに、インデックスを再作成しました:ここ

DELETE FROM table where id IN (1, 2, 3);のANALYZE EXPLAINのサンプルです。

テストではすべての行が削除され、通常は選択基準があるのでTRUNCATEを使用していませんが、テスト目的ですべての行が含まれています。トリガを有効にすると、各行を1つずつ削除すると193,616ミリ秒かかりましたが、バッチ削除は285,558ミリ秒かかりました。その後、トリガーを無効にして、単列の削除で93,793ms、バッチ削除で181,537msを得ました。トリガーは値を合計して別のテーブルを更新します。基本的には簿記です。

低バッチサイズ(100と1)で遊んだことがありますが、それらはすべてパフォーマンスが悪いようです。

EDIT:ANALYZE delete from table where id=?とEXPLAIN:

Delete on table (cost=0.00..8.31 rows=1 width=6) (actual time=0.042..0.042 rows=0 loops=1) 
    -> Index Scan using pk_table on table (cost=0.00..8.31 rows=1 width=6) (actual time=0.037..0.037 rows=0 loops=1) 
     Index Cond: (id = 3874904) 
Total runtime: 0.130 ms 

EDIT:Hibernateのログを、行によって、単一の行のために回したが、それは基本的にやって削除リストが実際万IDが含まれている場合、Postgresが希望の場合、興味がありました違うことをする:いいえ。

Delete on table (cost=6842.01..138509.15 rows=9872 width=6) (actual time=17.170..17.170 rows=0 loops=1) 
    -> Bitmap Heap Scan on table (cost=6842.01..138509.15 rows=9872 width=6) (actual time=17.160..17.160 rows=0 loops=1) 
     Recheck Cond: (id = ANY ('{NUMBERS 1 THROUGH 10,000}'::bigint[])) 
     -> Bitmap Index Scan on pk_table (cost=0.00..6839.54 rows=9872 width=0) (actual time=17.139..17.139 rows=0 loops=1) 
       Index Cond: (id = ANY ('{NUMBERS 1 THROUGH 10,000}'::bigint[])) 
Total runtime: 17.391 ms 

EDIT:上記の、EXPLAIN ANALYZEをもとに、私は実際の削除操作から、いくつかのログを取得しました。以下は、1行1行削除の2つのバリエーションのロギングです。ここ

は、いくつかの単一の削除である:ここ

2013-03-14 13:09:25,424:delete from table where id=? 
2013-03-14 13:09:25,424:delete from table where id=? 
2013-03-14 13:09:25,424:delete from table where id=? 
2013-03-14 13:09:25,424:delete from table where id=? 
2013-03-14 13:09:25,424:delete from table where id=? 
2013-03-14 13:09:25,424:delete from table where id=? 
2013-03-14 13:09:25,424:delete from table where id=? 
2013-03-14 13:09:25,424:delete from table where id=? 
2013-03-14 13:09:25,424:delete from table where id=? 
2013-03-14 13:09:25,424:delete from table where id=? 

は、単一の削除の他の変形例である(リストは単に1つのアイテムである)

2013-03-14 13:49:59,858:delete from table where id in (?) 
2013-03-14 13:50:01,460:delete from table where id in (?) 
2013-03-14 13:50:03,040:delete from table where id in (?) 
2013-03-14 13:50:04,544:delete from table where id in (?) 
2013-03-14 13:50:06,125:delete from table where id in (?) 
2013-03-14 13:50:07,707:delete from table where id in (?) 
2013-03-14 13:50:09,275:delete from table where id in (?) 
2013-03-14 13:50:10,833:delete from table where id in (?) 
2013-03-14 13:50:12,369:delete from table where id in (?) 
2013-03-14 13:50:13,873:delete from table where id in (?) 

は、両方のテーブルに存在するIDとすべきです逐次的である。


DELETE FROM table WHERE id = 3774887;

Delete on table (cost=0.00..8.31 rows=1 width=6) (actual time=0.097..0.097 rows=0 loops=1) 
    -> Index Scan using pk_table on table (cost=0.00..8.31 rows=1 width=6) (actual time=0.055..0.058 rows=1 loops=1) 
     Index Cond: (id = 3774887) 
Total runtime: 0.162 ms 

のEXPLAIN ANALYZEでは有意差が考慮さ0.452対DELETE FROM table WHERE id IN (3774887);

Delete on table (cost=0.00..8.31 rows=1 width=6) (actual time=0.279..0.279 rows=0 loops=1) 
    -> Index Scan using pk_table on table (cost=0.00..8.31 rows=1 width=6) (actual time=0.210..0.213 rows=1 loops=1) 
     Index Cond: (id = 3774887) 
Total runtime: 0.452 ms 

0.162のANALYZE EXPLAIN?

EDIT:50,000および休止状態へ

セットバッチサイズは、そのアイデアのようにしませんでした:

java.lang.StackOverflowError 
     at org.hibernate.hql.ast.util.NodeTraverser.visitDepthFirst(NodeTraverser.java:40) 
     at org.hibernate.hql.ast.util.NodeTraverser.visitDepthFirst(NodeTraverser.java:41) 
     at org.hibernate.hql.ast.util.NodeTraverser.visitDepthFirst(NodeTraverser.java:42) 
.... 
+0

単一行を削除すると、他のインデックスが使用されているようです。それを確認できますか? – ntalbs

+0

1行ずつ削除に関する情報を追加しました。テーブルのMAX(id)は3774904なので、私はちょうどより大きな数字を選んだだけです。私は3774904以下のIDで同じことを試みましたが、テーブルにはなかったし、同様の結果が得られました(テーブルのIDと同様の結果)。 – nevets1219

+1

はい、0.162対0.452 *は0.452が0.162よりほぼ3倍長いため重要です。説明は、彼らが同じことをうまくやっていることを示唆していることは特に興味深い。私はpostgresqlチームでこれを上げます。 [pgsql-general](http://www.postgresql.org/community/lists/)はおそらく良い場所です –

答えて

5

[OK]を、あなたは注意する必要がある最初の事はSQL計画に変換を取得しなければならないことです何らかの方法で。あなたのEXPLAINの結果は、ここでのロジックは、IN(vals)のコンストラクトと比較して、等価では根本的に異なることを示唆しています。

WHERE id = 1; 

単純な等価フィルタに変換されます。どうやらプランナーは、配列が正確に一つのメンバーを有する場合、これらは数学的に同一であることに気づくために十分にスマートではありません

WHERE id = ANY(ARRAY[1]); 

WHERE id IN (1); 

は、配列の一致に変換されます。だから、ネストループのビットマップインデックススキャンを取得する理由は、任意のサイズの配列を計画することです。

ここで興味深いのは、それが遅いということだけではなく、ほとんどの場合パフォーマンスが向上していることです。したがって、in()句の1つのメンバーは40倍遅く、10000のメンバーでは170倍の遅さですが、10000メンバーバージョンも50倍になります。 10000回の個別インデックススキャンon id。

ここで何が起きているのは、多数のIDがチェックされているときにパフォーマンスが向上するプランが選択されているが、少数しかないときはパフォーマンスが低下するということです。

+0

配列サイズを増やし、何が起こったかを報告しようとします!バッチサイズを選ぶための一般的なガイドラインはありますか? – nevets1219

+0

Hibernateは50,000を好きではなかったようです(スタックオーバーフローエラーを出しました) – nevets1219

+0

あなたが書いたものを再読み込みしました - 私は 'WHERE id = 1;'(これをAと呼ぶ)と 'WHERE id IN(1); (これをBと呼ぶことにします)、後者は遅くなります。 Aを実行するときにあなたを正しく理解しているのであれば、Bを実行するのと同じですが、Bを実行するときに、実行時に別のプラン(大きなIDのリストを好むもの)を使用することができます(つまり、BはA )。私はAとBの両方を実行し、同じプランを生成します。おそらく、Hibernateは何かをしていますか? – nevets1219

4

ここでの問題は、「できるだけ早く多くのレコードを削除するにはどうすればよいですか? DELETE ... IN()メソッドは、個々の行の削除よりも優れているので、IN(?)のメンバーが1つの場合の理由が=よりも遅いように見える理由を追求します。あなたを助けるつもりはありません。

削除したいすべてのIDを保持してから、1回の削除を実行するために一時テーブルを使用することを検討する価値があります。

あまりにもコストがかからない場合は、リスト内のIDが非常に大きい順に並べると、非常に大きな削除パフォーマンスが得られる場合があります。あなたがそれらをソートする必要がある場合は気にしないでくださいが、削除の各バッチがインデックスの同じ領域にクラスタ化されているIDのアドレスを保証する方法がわずかに有益かもしれません。

いずれにしても、索引が使用されていて同じ計画が両方のケースで生成されているように見えるので、実際には削除操作自体の問題ではなく、 。私は内部について、私が恐れているかどうかを十分に知りません。

関連する問題