通常のMariaDBインストールとMariaDB Galeraクラスタの動作に矛盾があります。ガレラクラスターでのINSERT ... SELECTステートメントでロックが期待通りに機能しません。これにより、アプリケーションに重複IDが発生します。MariaDB Galera Cluster:INSERT ... SELECTのロックが壊れていますか?
すべての接続は、このクエリで確認分離レベルREPEATABLE-READ(デフォルト)、使用:テスト用
SELECT * FROM information_schema.session_variables WHERE variable_name = 'tx_isolation';
セットアップ:
CREATE TABLE `TestTab` (`id` int(10) unsigned NOT NULL, `name` varchar(100) NOT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8;
期待される動作を:二つの並列接続は、2つのレコードを作成する必要があります'id'フィールドには2つの異なる値があります。通常のMariaDBのインストール(非クラスタ)と予想される動作を実証
コマンドシーケンス:
Connection 1 Connection 2
SET autocommit=0;
START TRANSACTION;
INSERT INTO TestTab (`id`, `name`)
SELECT IFNULL(max(id)+1, 1), 'insert 1' FROM TestTab Limit 0,1;
SET autocommit=0;
START TRANSACTION;
INSERT INTO TestTab (`id`, `name`)
SELECT IFNULL(max(id)+1, 1), 'insert 2' FROM TestTab Limit 0,1;
-- *** BAD *** INSERT completes immediately, ignoring connection 1
COMMIT;
COMMIT;
SELECT * FROM TestTab;
SELECT * FROM TestTab;
SELECT * FROM TestTab;
+----+----------+
| id | name |
+----+----------+
| 1 | insert 1 |
| 1 | insert 2 | -- !!! same id for both records !!! --
+----+----------+
:MariaDBガレラクラスタに問題があることを実証し
Connection 1 Connection 2
SET autocommit=0;
START TRANSACTION;
INSERT INTO TestTab (`id`, `name`)
SELECT IFNULL(max(id)+1, 1), 'insert 1' FROM TestTab Limit 0,1;
SET autocommit=0;
START TRANSACTION;
INSERT INTO TestTab (`id`, `name`)
SELECT IFNULL(max(id)+1, 1), 'insert 2' FROM TestTab Limit 0,1;
-- *** GOOD *** INSERT blocks (ensures repeatable read for the SELECT)
COMMIT;
-- only now connection 2 completes the the INSERT
COMMIT;
SELECT * FROM TestTab;
SELECT * FROM TestTab;
+----+----------+
| id | name |
+----+----------+
| 1 | insert 1 |
| 2 | insert 2 |
+----+----------+
コマンドシーケンス明らかに、Galeraクラスタの問題は、両方のレコードが 'id'フィールドに同じ値を取得することです。 Galeraクラスタで、一意のIDを生成するINSERT ... SELECTパターンが壊れています。
私にはこれはバグのようですが、少なくとも非常に望ましくない予期しない動作に似ています。
これはGaleraクラスタの別の設定で修正できるものですか?
両方のセッションで分離レベルをSERIALIZABLEに上げようとしましたが、問題が修正されました。しかし、これは非常に望ましくない回避策であり、パフォーマンスはここで問題になります。そして、とにかく私は運が良かったようです。SERIALIZABLE分離レベルは、同じノードで発行されたトランザクション間でのみ行われるため、回避する必要があります。(quote from the Galera Cluster documentation)
どのような回避策をお考えですか?
これをMariaDBのバグとして報告してください。
ありがとうございました。 INSERT INTO TestTab( 'id'、' name') SELECT IFNULL(max(id)+1)1次のようにINSERT ... SELECT文の中に直接 "FOR UPDATE" 、 'INSERT 1'からTestTab Limit 0,1 FOR UPDATE; このシナリオでは、これは多くのロックを引き起こし(他の誰かが試した)、パフォーマンスが低下するようです。 – StaticNoiseLog
私の腸は十分ではないと言います。 'max(id)+ 1 'は他のノードのアクションを考慮しません。 _Perhaps_「COMMIT」は、そのような行為が最大で混乱するならば中止しますが、わかりません。 –