3

私はマスターマスターレプリケーションでいくつかのテストを行っていますが、奇妙な問題が発生しました。誰かが問題を再現できるように、実行した手順を説明します。

2つのVMとそれぞれの構成ファイルでレプリケーションを設定しました。

-- Master1 -- 
auto_increment_increment = 2
auto_increment_offset = 1

-- Master2 -- 
auto_increment_increment = 2
auto_increment_offset = 2

これらの設定により、自動インクリメント列の等差数列が生成されます。

- Master1: 1,3,5,7,9,11,13  ...
- Master2: 2,4,6,8,10,12,14 ...

Master1は奇数を取得し、Master2は偶数を取得します。次に、テストデータベースを作成し、次の定義を持つテーブルを追加します。

CREATE TABLE `t1` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `c1` varchar(50) DEFAULT NULL,
 `d1` date DEFAULT '1970-01-01',
  PRIMARY KEY (`id`)
) ENGINE=MyISAM;

もちろん、データベースは両方のサーバーで作成されます。その後、実行します

START SLAVE;

レプリケーションが開始されるように両方のサーバーで。データを生成するために、私は次の手順を使用します。

  • プロセスを開始するには、単一のレコードを挿入する必要があります

    INSERT INTO t1(c1、d1)SELECT LPAD(''、50、MD5(RAND()))、DATE_ADD(CURDATE()、INTERVAL FLOOR(RAND()* 365)DAY);

  • 次に、同じテーブルからINSERT-SELECTを使用します。これにより、2 nの割合で挿入が開始されます。nは、クエリを実行する回数です。

    INSERT INTO t1(c1、d1)SELECT LPAD(''、50、MD5(RAND()))、DATE_ADD(CURDATE()、INTERVAL FLOOR(RAND()* 365)DAY)FROM t1;

ヒント:ここで説明するこの方法は、テーブルのランダムデータを生成するのにも非常に便利です。

したがって、両方のサーバーでこれらのクエリを同時に実行し始めると、常に自動インクリメント列のレプリケーション重複キーエラーが発生します。誰かアイデアがあればありがたいです!

PS:もちろん、この種のクエリは本番アプリケーションではめったに発生しませんが、それでもポイントを証明していると思います。

4

1 に答える 1

5

注:私は答えを見つけました、そして私はそれを一番上に置きました。答えの下には、これを説明するための価値をまだ保持している可能性のある他のいくつかの暴言(私の最初の答え)があります。

クエリは行数を2倍にするため、ステートメントINSERT INTO t1(c1,d1) SELECT LPAD('', 50, MD5( RAND() ) ), DATE_ADD( CURDATE(), INTERVAL FLOOR( RAND() * 365 ) DAY ) FROM t1;はサーバー1とサーバー2に異なる数の行を挿入できます。自動インクリメント列を使用するすべてのステートメントは、レプリケーションとともにINSERT_IDを送信し、その値はサーバー2では真になりません。ステートメントがそこでも実行されている場合。

例を見てみましょう。stop slave長時間実行されるクエリまたは不良ネットワークをシミュレートするために行います。

  1. 2つのデータベースを作成し、マスターマスターレプリケーションを設定します
  2. テーブルを作成し、最初の行を挿入します
  3. サーバー2でレプリケーションを停止します
  4. サーバー1で行数を2倍にするステートメントを実行します。2で十分ですが、3を実行しました。
  5. チェックしてくださいshow binlog events(警告、古いデータベースではこれを行わないでください。永遠にかかります)。これが私が見ているものです。

    クエリ| BEGIN
    Intvar | INSERT_ID=3
    クエリ| 使用test; INSERT INTO t1(c1、d1)SELECT...
    クエリ| COMMIT
    クエリ| BEGIN
    Intvar | INSERT_ID=5
    クエリ| 使用test; INSERT INTO t1(c1、d1)SELECT...
    クエリ| COMMIT
    クエリ| BEGIN
    Intvar | INSERT_ID=9
    クエリ| 使用test; INSERT INTO t1(c1、d1)SELECT...クエリ| 専念

  6. 複製を実行するたびに、それに応じてINSERT_IDが変更されることに注意してください。2番目の挿入では5であり、最初の挿入が1行挿入されたことを意味します(増分は2であることを忘れないでください)。3番目の挿入ではINSERT_IDは9であり、2番目の挿入で2行が挿入されたことを意味します。これはすべて理にかなっています。続けましょう

  7. サーバー2で複製を1回実行し、まだ複製を開始しないでください。select * from t1これを正しく実行すると、ID1と2の2つの行が表示されます。

  8. 次に、スレーブを再度起動して、を実行しSHOW SLAVE STATUS \Gます。重複ID5で停止しました。t1からすべての値を選択すると、4行が再び表示されます。最初のものは最初のものでした。2つ目は、サーバー2で行ったことであり、ID 3と5の最後の2つは、サーバー1の最初のステートメントからのもので、1行だけを追加したものです。

  9. レプリケーションの次の部分はこれです

    クエリ| BEGIN
    Intvar | INSERT_ID=5
    クエリ| 使用test; INSERT INTO t1(c1、d1)SELECT...
    クエリ| 専念

  10. これが発生したとき、サーバー1ではINSERT_IDは5であり、これがレプリケーションで使用されますが、サーバー2ではすでにID 5が存在するため、これを取得する前に行をもう一度複製しました。したがって、レプリケーションは中断します。

結論はこれです。マスターマスターレプリケーションを実行する場合、各ステートメントは同じようにデータベースに影響を与える必要があります。特に同じ数の行を追加または削除します。

そうは言っても、このようなことをする必要がある場合は、この特定のケースを簡単に修正できます。

  1. server_idをデータに追加し、次のようなテーブルを作成します

    CREATE TABLE t1idint(11)NOT NULL AUTO_INCREMENT、 server_idint(1)DEFAULT NULL、 c1varchar(50)DEFAULT NULL、 d1date DEFAULT '1970-01-01'、PRIMARY KEY(id))ENGINE = MyISAM AUTO_INCREMENT = 4 DEFAULT CHARSET = latin1 ;

  2. サーバーIDごとに1つずつ、2つの行を準備します

    INSERT INTO t1(server_id、c1、d1)SELECT 1、LPAD(''、50、MD5(RAND()))、DATE_ADD(CURDATE()、INTERVAL FLOOR(RAND()* 365)DAY); INSERT INTO t1(server_id、c1、d1)SELECT 2、LPAD(''、50、MD5(RAND()))、DATE_ADD(CURDATE()、INTERVAL FLOOR(RAND()* 365)DAY);

  3. 複製ごとにサーバーで作成された行を考慮に入れてください。

    INSERT INTO t1(server_id、c1、d1)SELECT server_id、LPAD(''、50、MD5(RAND()))、DATE_ADD(CURDATE()、INTERVAL FLOOR(RAND()* 365)DAY)FROM t1 where server_id = 1;

以下は元の回答です

まず、1、3、5、..と2、4、6の範囲の2セットのIDがあると仮定すると、間違っています... Auto_incrementが常にである場合、どのサーバーでステートメントが実行されるかに関係なく、 max(id)+1。したがって、サーバー1で2つの挿入を行うと、1と3の奇数の値が得られます。サーバー2で1つの挿入を行うと、4の偶数の値が得られます(4は、auto_increment_offset+を満たす3より大きい次の数値です。 N×auto_increment_increment)。

Auto_incrementの値は、を実行して確認できます。show table status;

次に、最初の挿入の後に挿入するたびに、テーブルの行数が2倍になり、操作が非常に遅くなります。これが、各クエリが非常に遅いことと関係がある場合でも、驚くことではありません。

そうは言っても、これが私がこれをテストした方法です(そして同じ驚くべき結果が得られました)。

  1. 2台のサーバーとマスターマスターを使用して、新しい空のセットアップを作成しましたmake_replication_sandbox --master_master mysql-5.5.17-osx10.6-x86_64.tar.gz。それらは両方とも開始されており、スレーブもあります。セットアップと同じように自動的に構成されます。
  2. 次に、テーブルを作成し、質問に従って最初の行を挿入しました。Auto_incrementは両方のサーバーで2になり、テーブルに1つの行があります
  3. while (true) do ./n1 test -e "INSERT INTO t1(c1,d1) SELECT LPAD('', 50, MD5( RAND() ) ), DATE_ADD( CURDATE(), INTERVAL FLOOR( RAND() * 365 ) DAY ) FROM t1;"; done;次に、両方のサーバーに対して同時に実行しています(もう一方のサーバーでは./n2)。

そして私には理論があります。

テーブルに1000行あり、両方のサーバーで同時に同じ複製を開始するとします。完璧に言えば、後で両方のサーバーで4000行を取得し、それらはすべて同じになります。

ただし、各データベースの行を複製して、サーバー1が2000行とサーバー2000行を認識できるようにしますが、最初の1000のみが同じであり、他の1000は2つのサーバーで異なる方法で生成されています。

次に、レプリケーションが開始されます。これはステートメントベースのレプリケーションであるため、同じステートメントが実行されます。つまり、両方のサーバーで行が再び4000に複製されます。これは正しいカウントですが、同じ1000だけで、他の3000は異なります。

各サーバーが同じ数のクエリを実行している限り、これは機能する可能性があります(重複はありませんが、データは異なります)が、レプリケーションがキャッシュアップする前に1つのサーバーが2つのクエリを実行できた場合、サーバー2で1000行が追加されたというステートメントがレプリケーションで取得されます(以前に1000行あった場合)が、サーバー1では4000行が追加されます(サーバー1はすでに1000行を2倍にしているため)。次のステートメントがサーバー2にさらに2000行を追加し、バイナリログに「サーバーで使用される最初の自動増分」のようなものが含まれている場合、衝突が発生します。

私はこれが抽象的で奇妙であり、それについて考えるよりも書くのがさらに難しいことを知っています:)

これがお役に立てば幸いです。これが問題になることを願っています...マスターマスターは難しいです。これは間違いなくマスターマスターではやらないことの1つです。

于 2011-11-19T23:46:49.283 に答える