3

次のような単純化されたテーブル構造が与えられます。

 CREATE TABLE t1 (
        id INT,
        num INT,
        CONSTRAINT t1_pk
        PRIMARY KEY (id),
        CONSTRAINT t1_uk
        UNIQUE (id, num)
    )

このようなサブクエリを使用して、競合状態を引き起こさずにレコードを挿入できますか?

INSERT INTO t1 (
    id,
    num
) VALUES (
    1,
    (
        SELECT MAX(num) + 1
        FROM   t1
    )
)

それとも、サブクエリはアトミックではありませんか? INSERT同時にs が同じ値を取得しnum、一意の制約違反を引き起こすことを心配しています。

4

3 に答える 3

8

はい、これは間違いなく競合状態を引き起こす可能性があります。これは、すべてのステートメントがアトミックであることが保証されているため、クエリの実行の個別の部分で変更されていないデータ セット全体で操作する必要がないためです。

クライアントが上記のクエリを送信します。エンジンがMAX(num)他のリーダーと互換性のあるロックのみを保持している間に を検出する限り、別のクライアントは が実行されるMAX(num)前に同じものを検出できますINSERT

私が知っているこの問題を回避する方法は 4 つあります。

  1. シーケンスを使用します。では、挿入する次の一意の番号を返すINSERTことができます。sequencename.nextval

    SQL> create sequence t1num;
    
    Sequence created.
    
    SQL> select t1num.nextval from dual;
    
       NEXTVAL
    ----------
             1
    
    SQL> select t1num.nextval from dual;
    
       NEXTVAL
    ----------
             2
    
  2. 失敗時に再試行します。INSERT1 秒あたりのトランザクション数が非常に多いシステムに関する信頼できる記事を読みました。このシステムは、このシナリオとまったく同じではありませんが、間違った値を使用している可能性があるという同じ競合状態に苦しんでいます。num彼らは、最初に一意の制約を与え、次に通常どおり続行することで最高の TPS が達成されることを発見しました。一意の制約INSERTの違反が原因で拒否された場合、クライアントは単純に再試行します。

  3. INSERTが完了するまで、エンジンが他のリーダーをブロックするように強制するロック ヒントを追加します。これは技術的には簡単かもしれませんが、おそらく高い同時実行性には適していません。がMAX()1 回のシークで実行され、ブロッキングが長くなく、多くのクライアントをブロックしない場合、理論的には許容できますが、ほとんどのシステムは時間の経過とともに大きくなり、すぐに危険になります。

  4. 別の 1 行のヘルパー テーブルを使用して、 の次/最新の値を記録しますnumUPDATEヘルパー テーブルで同時読み取りを実行し、読み取った値INSERTをメイン テーブルに個別に使用します。私の考えでは、これには単一のクエリではないという煩わしさがあります。さらに、クライアントが の値を「予約」numできたが、何らかの理由で を実際に実行できなかった場合INSERT、ギャップが発生する可能性があるという問題があります。表の値でnum

于 2013-04-01T19:35:49.103 に答える
0
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
INSERT INTO t1 (id, num) VALUES (1, (SELECT MAX(num) + 1 FROM t1));
COMMIT;

また

LOCK TABLE t1 IN EXCLUSIVE MODE;
INSERT INTO t1 (id, num) VALUES (1, (SELECT MAX(num) + 1 FROM t1));
COMMIT;

どちらも、同じ操作を実行する同時プロセスでパフォーマンスの問題を引き起こします。しかし、保証されたギャップのないシーケンスが要件である場合、これはコストです。

于 2013-04-01T20:48:23.603 に答える