アップデート:
コメントを参照してください。これはMySQL5.5で修正されているようです。これらの例では、まだテーブルロックがあり、インデックスの次のキーロックをだますことはできません。
オリジナル:
昨日あなたの質問を見つけました、そして私はInnoDbのMVCCseriabilityモデルについても疑問に思いました。
だから私はいくつかのテストを行いました。MySQL5.1.37。直列化可能性の問題の良いテストは、postgrESQL 9.0 MVCCドキュメントで提供されているものです。この章では、直列化可能分離と真の直列化可能性について、述語ロックが実行されていない場合の直列化可能性に関するMVCCモデルの制限を確認できます。
それでは、MySQLでテストしてみましょう。
CREATE TABLE t1 (
class integer,
value integer
) ENGINE=InnoDB;
INSERT INTO t1 (`class`,`value`) VALUES
(1,10),
(1,20),
(2,100),
(2,200);
次に、2つの異なる接続を開いて、2つの並列トランザクション(T1とT2)を作成します。
T1:
SET TRANSACTIOn ISOLATION LEVEL SERIALIZABLE;
BEGIN;
SELECT SUM(value) FROM t1 WHERE class = 1;
結果は30です。
T2:
SET TRANSACTIOn ISOLATION LEVEL SERIALIZABLE;
BEGIN;
SELECT SUM(value) FROM t1 WHERE class = 2;
結果は300です。
次に、直列化可能性の問題が発生します。T1がT2からの選択を無効にする行を挿入した場合(ここではT2も同じことを行います)。
T1:
INSERT INTO t1 (`class`,`value`) VALUES (2,30);
==>待機中(ロックが設定されています)
T2:
INSERT INTO t1 (`class`,`value`) VALUES (1,300);
==>エラー1213(40001):ロックを取得しようとしたときにデッドロックが見つかりました。トランザクションを再開してみてください
T1は挿入に成功し、t2にはロールバックがあり、直列化可能性が良好です。
これはPostgreSQL9.0では失敗します(9.1では状況が変わりますが、別の問題です)。実際、テーブルへの挿入を実行できるのは1つのトランザクションのみです。で挿入しようとしても。class=3
INSERT INTO t1 (`class`,`value`) VALUES (3,30);
待機中のロックと、問題が発生した場合のデッドロックが表示されます。MySQLに述語ロックがあるように見えます...しかし実際には、これはInnoDBの次のキーロックの実装です。
Innodbは、インデックスに対してもいくつかのギャップをロックして行ロックを実行します。ここでは、テーブルにインデックスがありません。MySQLがテーブルをロックすることを決定したようです。
それでは、次のキーのロックをテストして、これが直列化可能性を強制するかどうかを確認してみましょう。最初に実行中のトランザクションをロールバックします(T1)。次に、インデックスを作成します。
CREATE index t1class ON t1 (class);
次に、テストをやり直します。成功、直列化可能性は引き続き適用されます。朗報です。
しかし、インデックスが設定されていると、次のキーのロックと行のロックがインデックスで行われると思います。これは、並列トランザクションに影響を与えない場合に挿入を実行できるはずであることを意味します...そしてここに大きな問題があります。
T1:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN;
SELECT SUM(value) FROM t1 WHERE class = 1;
結果は30です。
T2:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN;
SELECT SUM(value) FROM t1 WHERE class = 2;
結果は300です。
ここでは、T1に無関係な挿入を行います。これで、インデックスが作成され、これが成功します。
T1:
INSERT INTO t1 (`class`,`value`) VALUES (3,30);
どちらも挿入を実行できます(ここでは1つだけ作成しました)。これは正常です。予測ロックは適用されません。SELECTクエリは実行されていませんclass=3
。次のキーのロックは、適切なインデックスを指定するとパフォーマンスが向上するように見えます(挿入にテーブルロックがない)。
次に、次のキーのロックに挿入しようとします。T2(class = 2)の選択に一致する行に:
T1:
INSERT INTO t1 (`class`,`value`) VALUES (2,30);
痛い。成功します!
T2:
INSERT INTO t1 (`class`,`value`) VALUES (1,300);
==>待っています。そこにはまだ鍵があります。うまくいけば。
T1:
COMMIT;
T2 :(ロックが解除されたところで挿入が行われます)
SELECT SUM(value) FROM t1 WHERE class = 2;
COMMIT;
ここにはまだ300があります。直列化可能性がなくなったようです。
select * from t1;
+-------+-------+
| class | value |
+-------+-------+
| 1 | 10 |
| 1 | 20 |
| 2 | 100 |
| 2 | 200 |
| 3 | 30 | <-- test
| 2 | 30 | <-- from trans1
| 1 | 300 | <-- from trans2 ERROR!
+-------+-------+
結果:並列トランザクションクエリに影響を与える行を挿入する前に、関連のない新しい行を挿入することにより、次のキーのロックメカニズムをスプーフィングしました。または、少なくともこれは私が私のテストから理解していることです。したがって、真の直列化可能性についてエンジンを信頼しないでください。トランザクションに集計関数がある場合、最良の方法は、テーブルを手動でロックし、直列化可能性の問題を実際の1人だけの状況に変換することです。驚くことではありません。例における他の直列化可能性の問題は、制約の検証(操作後も量がまだ正であることを確認する)です。これらの場合にもロックを所有していますか。