2009-06-07 14 views
19

PostgreSQL 8.3をストレージバックエンド(Pythonとpsycopg2を使用)として使用する、特定のアプリケーションがあります。重要なテーブルに対して実行する操作は、挿入または更新の大多数(ほとんどは削除または選択)にあります。PostgreSQLの更新/置換操作をどのように高速化できますか?

私たちは正当な理由から、適切に動作する独自のData Mapperレイヤーを作成しましたが、1つの大きなボトルネック、つまり更新のパフォーマンスがあります。もちろん、私はupdate/replaceシナリオが '空のテーブルへの挿入'のようにスピーディーであるとは思っていませんが、ちょっと近づけばいいですね。このシステムは、私は私のテストで単語「置き換える」を使用する用語で見ることができる我々は常に更新時に各列のすべてのフィールドを設定して同時更新

から自由であることを

注意。

  1. を更新するために、行の配列をとるreplace()プロシージャを作成します:私はこれまで、当社の更新問題への2つのアプローチを試してみた

    CREATE OR REPLACE FUNCTION replace_item(data item[]) RETURNS VOID AS $$ 
    BEGIN 
        FOR i IN COALESCE(array_lower(data,1),0) .. COALESCE(array_upper(data,1),-1) LOOP 
         UPDATE item SET a0=data[i].a0,a1=data[i].a1,a2=data[i].a2 WHERE key=data[i].key; 
        END LOOP; 
    END; 
    $$ LANGUAGE plpgsql 
    
  2. insert_or_replaceルールを作成するようにすべてのものが、時折削除となる多列が挿入

    CREATE RULE "insert_or_replace" AS 
        ON INSERT TO "item" 
        WHERE EXISTS(SELECT 1 FROM item WHERE key=NEW.key) 
        DO INSTEAD 
         (UPDATE item SET a0=NEW.a0,a1=NEW.a1,a2=NEW.a2 WHERE key=NEW.key); 
    

のThすべてのテストは、データベースと同じコンピュータ上で実行されている

  • :テスト実行に関する

    Multi-row insert   : 50000 items inserted in 1.32 seconds averaging 37807.84 items/s 
    executemany() update  : 50000 items updated in 26.67 seconds averaging 1874.57 items/s 
    update_andres    : 50000 items updated in 3.84 seconds averaging 13028.51 items/s 
    update_merlin83 (i/d/i) : 50000 items updated in 1.29 seconds averaging 38780.46 items/s 
    update_merlin83 (i/u)  : 50000 items updated in 1.24 seconds averaging 40313.28 items/s 
    replace_item() procedure : 50000 items replaced in 3.10 seconds averaging 16151.42 items/s 
    Multi-row insert_or_replace: 50000 items inserted in 2.73 seconds averaging 18296.30 items/s 
    Multi-row insert_or_replace: 50000 items replaced in 2.02 seconds averaging 24729.94 items/s 
    

    ランダムノート:後者が遅くビットを挿入しますが、両方がアップデートに公平なビットをスピードアップESE住む。 localhostに接続しています。

  • 挿入と更新は、それぞれ独自のトランザクション(UPDATED)で送信された500項目のバッチでデータベースに適用されます。
  • すべての更新/置換テストでは、データベースにすでにある値と同じ値が使用されていました。
  • psycopg2 adapt()関数を使用して、すべてのデータがエスケープされました。
  • すべてのテーブルが切り捨てられ、使用前に真空ある(のみ切り捨てが起こった以前の実行中に、を追加しました)
  • テーブルは次のようになります。

    CREATE TABLE item (
        key MACADDR PRIMARY KEY, 
        a0 VARCHAR, 
        a1 VARCHAR, 
        a2 VARCHAR 
    ) 
    

だから、本当の問題は次のとおりです。更新/交換操作の速度をさらに上げるにはどうすればよいですか? (私はこれらの知見が「十分に良い」と思っていますが、私はSOの群衆をタップしないであきらめたくないと思っています:)

もう少しエレガントなreplace_item壊れていることが大歓迎です。

再現を試みる場合は、テストスクリプトはhereです。しかし、それを最初にチェックすることを忘れないでください...それはWorksForMeですが、...

dbを編集する必要があります。あなたの設定に合わせてconnect()行を追加してください。 freenodeの@ #postgresqlでアンドレスへ

EDIT

おかげで私は、単一のクエリ更新で別のテストを持っています。 (上記のupdate_andresとしてリストされている)複数行の挿入によく似ています。

UPDATE item 
SET a0=i.a0, a1=i.a1, a2=i.a2 
FROM (VALUES ('00:00:00:00:00:01', 'v0', 'v1', 'v2'), 
      ('00:00:00:00:00:02', 'v3', 'v4', 'v5'), 
      ... 
    ) AS i(key, a0, a1, a2) 
WHERE item.key=i.key::macaddr 

EDIT私は、インサート・ツー・温度で別のテストを持っている以下のfreenodeのと水差し/ JWP @ #postgresqlでmerlin83に

感謝を/削除/挿入アプローチ(update_merlin83」として記載されている(I/d/i)」)。

INSERT INTO temp_item (key, a0, a1, a2) 
    VALUES (
     ('00:00:00:00:00:01', 'v0', 'v1', 'v2'), 
     ('00:00:00:00:00:02', 'v3', 'v4', 'v5'), 
     ...); 

DELETE FROM item 
USING temp_item 
WHERE item.key=temp_item.key; 

INSERT INTO item (key, a0, a1, a2) 
    SELECT key, a0, a1, a2 
    FROM temp_item; 

私の直感では、これらのテストは、実際のシナリオでのパフォーマンスに非常に代表するものではないということですが、私は違いがさらなる調査のための最も有望なアプローチの指示を与えるのに十分素晴らしいと思います。 perftest.pyスクリプトには、チェックアウトしたい人のためのすべてのアップデートが含まれています。これは、しかし、かなり醜いですので、あなたのゴーグルを忘れないでください:) freenodeの@ #postgresqlで

アンドレスは、私のように記載されている、インサート・ツー・TEMP /更新バリアント(でテストする必要があることを指摘し

EDIT上記の「update_merlin83(i/u)」)。

INSERT INTO temp_item (key, a0, a1, a2) 
    VALUES (
     ('00:00:00:00:00:01', 'v0', 'v1', 'v2'), 
     ('00:00:00:00:00:02', 'v3', 'v4', 'v5'), 
     ...); 

UPDATE item 
SET a0=temp_item.a0, a1=temp_item.a1, a2=temp_item.a2 
FROM temp_item 
WHERE item.key=temp_item.key 

EDIT

おそらく最終編集: 私はより良い私たちの負荷のシナリオに一致するように私のスクリプトを変更し、数字が少し物事をスケールアップし、いくつかのランダム性を追加する場合でも保持するようです。誰かが他のシナリオと非常に異なる数字を得たら、それについて知りたいと思うでしょう。

+0

を助けるかもしれませんか?外国キー? –

+0

テストスクリプトにはありません。現実世界では、1つです。 –

+0

あなたの 'UPDATE'の' EXPLAIN ANALYZE'を投稿できますか?私はエスティメータが何を起こすべきかを知りたい。 – Sean

答えて

1

insert_or_replaceにあります。この方法を試してください。

WHERE EXISTS(SELECT 1 FROM item WHERE key=NEW.key LIMIT 1) 

代わり

WHERE EXISTS(SELECT 1 FROM item WHERE key=NEW.key) 

のコメントで述べたように、それはおそらく何もしません。私が追加しなければならないのは、インデックスを削除してINSERT/UPDATEのパフォーマンスを向上させることができるということです。これは、あなたのテーブルがオーバーインデックスされていない限り、あなたがやりたいことではないかもしれませんが、少なくともチェックアウトする必要があります。

+0

おそらくそれは不要です - ドキュメントからの抜粋(http://www.postgresql.org/docs/current/static/functions -subquery.html#AEN15270): "サブクエリは一般に、少なくとも1つの行が返されたかどうかを判断するのに十分なだけ実行され、完了までには至りません。 –

+0

ああ、ありがとう。スマートな存在がどうだったのか分からなかった。今私がやります。 :) – chaos

+0

キーはユニークなので、1つの行だけを返します。それにもかかわらず、私は試してみましたが、いずれの方法でもパフォーマンスに顕著な変化はありませんでした。しかし、ありがとう! –

1

Oracleでは、テーブルをロックすると間違いなく助けになります。 PostgreSQLでもそれを試してみてください。

+0

私はすべてのトランザクションでロックされたテーブルを使ってすべてのテストを実行しようとしました。変化なし。 –

2

私は数ヶ月前にも同様の状況にあり、チューニングされたチャンク/トランザクションサイズから最大のスピードブーストを得ました。また、テスト中にチェックポイントの警告をログで確認し、適切に調整することもできます。

+0

私は確かにチェックポイントの警告を探すでしょう。ありがとう! –

2

UPSでWAL(Write Ahead Logging)を使用してディスク書き込み間の更新をキャッシュすることのメリットがあります。

wal_buffers この設定は、WAL(Write ahead Log)が持つことができるバッファ数を決定します。データベースに多くの書き込みトランザクションがある場合、この値をデフォルトよりも大きく設定すると、ディスク領域の使用率が向上します。実験し、決定する。良いスタートは256〜512Kのメモリに対応する約32〜64です。

http://www.varlena.com/GeneralBits/Tidbits/perf.html

4

私はPGでこれらの事を行う通常の方法は次のとおりです。一時テーブルのコピーを使用して(制約なし)、マージ(楽しい部分)、利益にターゲット表に一致する生データをロードします。私は特にこのような状況のためmerge_by_key機能を書いた

http://mbk.projects.postgresql.org/

をドキュメントがひどく友好的ではないですが、私はそれを良い外観を与えることをお勧めしたいです。

+0

一般的なプロセスのポイント: ネットワークの往復のコストを避け、ロードされた各行に対して複数のカーソル(ポータル)を作成しないようにtempにロードする(ええと、executemanyでは高速ですが、COPY wtf-pwns-it効率へのwrt)。 マージ関数/プロセスを使用して、挿入ステートメントのセマンティクスを変更するルール/トリガーを作成しないようにします。私はそれを両方の方法で行いました。明示的なので、常にマージプロセスを優先しました。 マージプロセスが十分に効率的でない場合は、インデックス停止(レクリエーション)/パーティション分割または http://pgfoundry.org/projects/pgbulkload/を参照する必要があります。 – jwp

関連する問題