web-dev-qa-db-ja.com

2つのMySQLテーブルを同期する方法(オンデマンドまたはcron経由)

私は Cron sync mysqlテーブル を見てきました。しかし、レプリケーションは使用できません。以前は percona-toolkit を使用していましたが、完全に機能しました。 syncコマンドはオンデマンドで実行することも、cron経由で実行することもできます。 2つのテーブルのチェックサムを比較し、挿入、更新、削除などを行います。ただし、Perl(DBD :: mysql)とMySQLには、これを実行する新しいサーバーとの互換性がないため、pt-table-を使用できません。同期。 Perl/DBD以外のものを使用する同様のソリューションはありますか?

編集:(詳細、明快さ)

  1. ソーステーブルとターゲットテーブルはどちらもライブテーブルであり、常に使用されています。したがって、テーブルが存在しないソリューション(たとえば、DROP TABLEが実行される)は受け入れられません。
  2. サーバー自体へのレプリケーションやそのような変更は使用できません。ソリューションはクライアント側で機能する必要があります。
  3. この特定のシナリオでは、両方のテーブルが同じサーバーにありますが、DBは異なります(例:db1.tbl db2.tbl)。ただし、この事実に依存しないソリューションは間違いなくボーナスになります
  4. ネットワーク遅延が問題になることはほとんどありません。この場合、スクリプトは同じデータセンター内のサーバーで実行されます。
  5. Perlを使用できない(PerlとMySQLの非互換性-64ビットと32ビット)
6
Anirudh

mysqldump --opt <database> <tablename>を使用してテーブルのダンプを作成し、新しいサーバーにフィードします。 TCP/IPを介してリモートデータベースにアクセスできるように見えるので、次のように使用できます。

mysqldump --opt --user=<youruser> --password=<yourpassword> -Host <yourhost> \
<yourDB> <yourtable> | mysql -u <newserveruser> -p<password>

リモートデータベースに接続し、それをダンプして、出力を新しいサーバーにフィードします。

リモートデータベースへの直接TCP/IPアクセスがなかった場合でも、 公開キー認証 を設定した後、SSHを介してデータをトンネリングすることで、ほとんど同じことができます。

ssh -C -l <remoteuser> <remoteserver> \
'mysqldump --opt --user=<youruser> --password=<yourpassword> <yourDB> <yourtable>' \
| mysql -u <newserveruser> -p<password>

詳細は mysqldumpのドキュメント および SSHのマニュアルページ を参照してください。

さらに帯域幅の効率が必要な場合は、mysqldumpを使用してダンプを作成し、それをソースサーバーに保存し、インポートする前にrsyncを使用して宛先サーバーで対応するものをコピー/更新することを検討してください。 rsyncはソースファイルと宛先ファイルに ローリングチェックサム を作成するため、以降の実行でダンプの内容のほとんどを転送する必要はない可能性があります。

行を挿入するときに一時テーブルを使用し、後でテーブルを元のテーブル名に変更してロック時間を短縮することを目的としたmysqldumpパッチがありましたが、未解決の問題があり、メインブランチに入れなかったので、実験的なものと見なします。 。パッチコードと詳細については この説明 を参照してください。

何らかの理由で宛先にテーブルを単にドロップできない場合は、ダンプされたデータを新しいテーブルに挿入する可能性があります(迅速でダーティですが、いくらか安全ではないアプローチでは、mysqldump出力をsed -e 's/mytable/newtable/g'にパイプしてから、さらにパイプしますto mysql)次に、このようないくつかのJOINを使用してUPDATE/DELETE/INSERTサイクルを実行します(untested、サニティチェックを実行します) ):

/* set write lock on the table so it cannot be read while updating */
LOCK TABLES mytable WRITE;

/* update all rows which are present in mytable and newtable */
UPDATE mytable AS M LEFT JOIN newtable AS N ON M.primarykey = N.primarykey 
SET M.column1=N.column1, M.column2=N.column2 [...]
WHERE N.primarykey Is Not NULL;

/* delete all rows from mytable which are no longer present in newtable */
DELETE M FROM mytable AS M LEFT JOIN newtable AS N on M.primarykey = N.primarykey 
WHERE N.primarykey Is NULL;

/* insert new rows from newtable */
INSERT INTO mytable (primarykey, column1, column2, [...]) 
SELECT (N.primarykey, N.column1, N.column2, [...]) FROM mytable AS M 
RIGHT JOIN newtable AS N ON M.primarykey=N.primarykey WHERE M.primarykey Is NULL

/* release lock */
UNLOCK TABLES;

注:もちろん、データベースのデータは、データを挿入/更新している間は一貫性がありませんが、トランザクションを使用していない限り(利用できません) MyISAMテーブルの場合)、これはどのような場合でも当てはまります。テーブルを削除して再作成すると、更新、削除、挿入のサイクルと同じように一時的な不整合が生じます。これは、MyISAMの非アトミックトランザクションレスデザインの性質によるものです。

11
the-wabbit

rubyrep のようにしたいようですが、これは左または右に同期でき、どちらの方法で同期させたいかを設定できます。しかし、それはデータベースレベルであり、テーブルレベルではないと思います。これは、テーブルベースの同期に変更するための良い出発点になるかもしれません。

別のオプションは、 http://codeinthehole.com/writing/how-to-sync-a-mysql-table-between-two-remote-databases/に示すようにテーブルをドロップする代わりにREPLACE INTOを使用することです。

ログにアクセスできない可能性があるようです。または、バイナリログからコマンドを取得することをお勧めします。

2
eby

トリガーを使用してみましたか?

DELIMITER $$
CREATE TRIGGER sync_table1_insert
AFTER INSERT ON `table1` FOR EACH ROW
BEGIN
    INSERT INTO table2 (id, value) VALUES (NEW.id, NEW.value);
END;
$$
DELIMITER ;

DELIMITER $$
CREATE TRIGGER sync_table1_update
AFTER UPDATE ON `table1` FOR EACH ROW
BEGIN
    UPDATE table2 SET value = NEW.value WHERE id = NEW.id;
END;
$$
DELIMITER ;

DELIMITER $$
CREATE TRIGGER sync_table1_delete
AFTER DELETE ON `table1` FOR EACH ROW
BEGIN
    DELETE FROM table2 WHERE id = OLD.id;
END;
$$
DELIMITER ;
0
Martin Carstens