1

私は次のようなテーブルを持っています:idx (PK) clmn_1

どちらもINTです。idx自動インクリメントとして定義されていませんが、私はそれをシミュレートしようとしています。このテーブルに挿入するために、私は使用しています:

"INSERT INTO  my_tbl (idx, clmn_1)   \
 SELECT IFNULL(MAX(idx), 0) + 1, %s  \
 FROM my_tbl", val_clmn_1

今、これは動作します。私が持っている質問は原子性についてです。同じテーブルを読み取ってから挿入するので、複数の挿入が同時に発生すると、キーの重複エラーが発生する可能性がありますか?

そして、どうすれば自分でテストできますか?

PerconaXtraDBサーバー5.5を使用しています。

4

1 に答える 1

2

これは、SELECTの実行中にmy_tblに共有ロックを作成するため、適切なソリューションではありません。任意の数のスレッドが同時に共有ロックを持つことができますが、同時書き込みロックをブロックします。したがって、これにより挿​​入がシリアル化され、SELECTが終了するのを待ちます。

このロックを観察できます。このクエリを1つのセッションで開始します。

INSERT INTO  my_tbl (idx, clmn_1) 
 SELECT IFNULL(MAX(idx), 0) + 1, 1234+SLEEP(60) 
 FROM my_tbl;

次に、別のセッションに移動してinnotopを実行し、ロック画面を表示します(キー「L」を押します)。次のような出力が表示されます。

___________________________________ InnoDB Locks ___________________________________
ID  Type    Waiting  Wait   Active  Mode  DB    Table   Index    Ins Intent  Special
61  TABLE         0  00:00   00:00  IS    test  my_tbl                    0         
61  RECORD        0  00:00   00:00  S     test  my_tbl  PRIMARY           0         

これが、自動インクリメントメカニズムがそのように機能する理由です。トランザクションの分離に関係なく、挿入スレッドは、自動インク番号をインクリメントするためにのみテーブルを一時的にロックします。これは非常に高速です。次に、ロックが解除され、他のスレッドがすぐに続行できるようになります。その間、最初のスレッドは挿入を終了しようとします。

自動インクリメントロックの詳細については、http://dev.mysql.com/doc/refman/5.5/en/innodb-auto-increment-handling.htmlを参照してください。

列を自動インクリメント列として定義するだけでなく、自動インクリメントの動作をシミュレートする理由がわかりません。 既存のテーブルを変更して自動インクリメントすることができます。


コメントを再確認してください。

PKが自動インクリメントとして宣言されている場合でも、値を指定できます。自動インクリメントは、INSERTでPK列を指定しないNULL場合、または値としてまたはを指定した場合にのみ開始DEFAULTされます。

CREATE TABLE foo (id INT AUTO_INCREMENT PRIMARY KEY, c CHAR(1));
INSERT INTO foo (id, c) VALUES (123, 'x'); -- inserts value 123
INSERT INTO foo (id, c) VALUES (DEFAULT, 'y'); -- inserts value 124
INSERT INTO foo (id, c) VALUES (42, 'n'); -- inserts specified value 42
INSERT INTO foo (c) VALUES ('Z'); -- inserts value 125
REPLACE INTO foo (id, c) VALUES (125, 'z'); -- changes existing row with id=125

コメントを再確認してください。

START TRANSACTION; 
SELECT IFNULL(MAX(idx), 0)+1 FROM my_tbl FOR UPDATE; 
INSERT INTO my_tbl (idx, clmn_1) VALUES (new_idx_val, some_val); 
COMMIT; 

これは実際には最初のアイデアよりも悪いです。これはSELECT...FOR UPDATE、がSロックではなくXロックを作成するためです。

SQLソリューションはACIDプロパティによって制限されるため、AUTO-INCREMENTの動作を再発明しようとしないでください。Auto-incは必然的にACIDの外部で機能します。

既存の行をアトミックに修正する必要がある場合は、REPLACEまたはINSERT ... ON DUPLICATEKEYUPDATEのいずれかを使用します。

于 2013-03-02T19:28:58.043 に答える