11

編集:私のデバッグとロギングのいくつかに基づいて、質問は、単一のIDがどこにあるDELETE FROM table WHERE id = xかよりもはるかに速い理由に要約されると思います。DELETE FROM table WHERE id IN (x)x

最近、バッチ削除と各行を1つずつ削除することをテストしましたが、バッチ削除の方がはるかに遅いことに気付きました。テーブルには削除、更新、挿入のトリガーがありましたが、トリガーがある場合とない場合でテストし、バッチ削除が遅くなるたびにテストしました。誰かがこれが事実である理由に光を当てたり、これをデバッグする方法に関するヒントを共有したりできますか?私が理解していることから、トリガーがアクティブになる回数を実際に減らすことはできませんが、「削除」クエリの数を減らすとパフォーマンスが向上すると当初は考えていました。

以下にいくつかの情報を記載しました。関連する情報がない場合はお知らせください。

削除は10,000のバッチで行われ、コードは次のようになります。

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つのインデックスがあります。カスケード操作は発生しません。

これがEXPLAINANALYZEのサンプルですDELETE FROM table where id IN ( 1, 2, 3 );

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

テストのためにデータをリロードするたびにバキュームしてインデックスを再作成しましたが、テストデータには386,660行が含まれています。

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

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

編集:Hibernateロギングをオンにし、行ごとの削除については、基本的に次のことを行っています:delete from table where id=?そしてEXPLAIN ANALYZE:

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

編集:リストに実際に10,000 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

編集:上記のEXPLAIN ANALYZEに基づいて、実際の削除操作からいくつかのログを取得しました。以下は、単一行ごとの削除の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

の説明分析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対0.452は有意差と見なされますか?

編集:

バッチサイズを50,000に設定すると、Hibernateはそのアイデアを気に入らなかった:

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)
....
4

2 に答える 2

5

わかりました、最初に注意しなければならないことは、SQL を何らかの方法でプランに変換する必要があるということです。EXPLAIN の結果は、ここでのロジックが IN(vals) 構造と比較して等値に対して根本的に異なることを示唆しています。

WHERE id = 1;

単純な等値フィルターに変換されます。

WHERE id IN (1);

次の配列一致に変換されます。

WHERE id = ANY(ARRAY[1]);

どうやら、プランナーは、配列が 1 つのメンバーしか持たない場合にこれらが数学的に同一であることに気付くほど賢くありません。そのため、ネストされたループのビットマップ インデックス スキャンを取得する理由は、任意のサイズの配列を計画することです。

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

つまり、ここで起こっていることは、プランナが、多数の ID がチェックされている場合はパフォーマンスが向上するが、少数しかない場合はパフォーマンスが低下するプランを選択しているということです。

于 2013-03-20T07:49:02.447 に答える
4

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

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

あまりコストがかからない場合は、リスト内の ID が昇順になるように配置すると、非常に大規模な削除のパフォーマンスに役立つ場合があります。それらを並べ替える必要がある場合は気にしないでください。ただし、削除の各バッチがインデックスの同じ領域にクラスター化されている ID を確実に削除する方法がある場合は、わずかに役立つ場合があります。

いずれにせよ、どちらの場合もインデックスが使用され、同じプランが生成されているように見えるので、削除アクション自体の問題ではなく、クエリの解析と最適化の問題が実際にここにあるのではないかと思います。私は恐れていることを確認するために内部について十分に知りません。

于 2013-03-20T08:32:00.527 に答える