sql >> データベース >  >> RDS >> PostgreSQL

多数のレコードに対して実行するのに永遠にかかる関数

    ほとんどの場合、競合状態に遭遇しています。 。関数を1000回連続して実行すると、個別のトランザクションが実行されます。 、次のようなことが起こります:

    T1            T2            T3            ...
    SELECT max(id) -- id 1
                  SELECT max(id)  -- id 1
                                SELECT max(id)  -- id 1
                                              ...
                  Row id 1 locked, wait ...
                                Row id 1 locked, wait ...
    UPDATE id 1
                                              ... 
    
    COMMIT
                  Wake up, UPDATE id 1 again!
                  COMMIT
                                Wake up, UPDATE id 1 again!
                                COMMIT
                                              ... 
    

    SQL関数として大幅に書き直され、簡略化されています:

    CREATE OR REPLACE FUNCTION get_result(val1 text, val2 text)
      RETURNS text AS 
    $func$
       UPDATE table t
       SET    id_used = 'Y'
            , col1 = val1
            , id_used_date = now() 
       FROM  (
          SELECT id
          FROM   table 
          WHERE  id_used IS NULL
          AND    id_type = val2
          ORDER  BY id
          LIMIT  1
          FOR    UPDATE   -- lock to avoid race condition! see below ...
          ) t1
       WHERE  t.id_type = val2
       -- AND    t.id_used IS NULL -- repeat condition (not if row is locked)
       AND    t.id = t1.id
       RETURNING  id;
    $func$  LANGUAGE sql;
    

    より多くの説明を伴う関連質問:

    説明

    • 2つの別々のSQLステートメントを実行しないでください。これはより高価であり、競合状態の時間枠を広げます。 1つのUPDATE サブクエリを使用する方がはるかに優れています。

    • 単純なタスクにはPL/pgSQLは必要ありません。あなたはまだできます PL / pgSQL、UPDATEを使用します 同じままです。

    • 競合状態から防御するには、選択した行をロックする必要があります。ただし、ドキュメントごと

    • 大胆な強調鉱山。幸い、min(id)を置き換えることができます 同等のORDER BYで簡単に / LIMIT 1 上記で提供しました。インデックスも同様に使用できます。

    • テーブルが大きい場合は、必要 idのインデックス 少なくとも。 idと仮定します すでにPRIMARY KEYとしてインデックス付けされています 、それは助けになるでしょう。ただし、この追加の部分的な複数列のインデックス おそらくもっと多く役立つでしょう :

      CREATE INDEX foo_idx ON table (id_type, id)
      WHERE id_used IS NULL;
      

    代替ソリューション

    アドバイザリーロック ここでは優れたアプローチかもしれません:

    または、一度に多くの行をロックすることもできます。 :




    1. 従属列のALTERTABLE

    2. Javaを使用したPostgresqlトランザクション処理

    3. XMLからSQLServerの日時に変換するときにミリ秒が間違っている

    4. PHPから呼び出されたmySQLの2ワードのフィールド名を選択します