2016-11-15 7 views
1

通常の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のバグとして報告してください。

答えて

1

GaleraはCOMMIT時まで他のノードにチェックインしないことに注意してください。一方、MAX(id)は、喜んで古い値になるものを取得し、誰かがMAX(id)を見るのを防ぐために何もしません。

可能な解決策(私はこれらのいずれかをテストし、また私は同じ問題と解決策がガレラせずに、単一のサーバに起こることができるかどうかを確認していませんでした。):

  • UNIQUE(id) - - これにより、COMMITが異常終了するはずです。
  • READ-COMMITTEDを使用して最新のMAX(id)をご覧ください。
  • INSERTから移動し、FOR UPDATEを使用してください。
+0

ありがとうございました。 INSERT INTO TestTab( 'id'、' name') SELECT IFNULL(max(id)+1)1次のようにINSERT ... SELECT文の中に直接 "FOR UPDATE" 、 'INSERT 1'からTestTab Limit 0,1 FOR UPDATE; このシナリオでは、これは多くのロックを引き起こし(他の誰かが試した)、パフォーマンスが低下するようです。 – StaticNoiseLog

+0

私の腸は十分ではないと言います。 'max(id)+ 1 'は他のノードのアクションを考慮しません。 _Perhaps_「COMMIT」は、そのような行為が最大で混乱するならば中止しますが、わかりません。 –

関連する問題