読者です 読者をやめる 読者になる 読者になる

カイワレの大冒険 Third

技術的なことや他愛もないことをたまに書いてます

MySQLのレプリケーションでエラーを吐いたときに、被害を最小限を抑える方法

トラブルシューティング トラブルシューティング-MySQL

MySQLに関わっていくと、レプリケーションは避けることはできないように思うんだけど、

だからと言って、そこにかける時間は最小限に抑えたいと思ったので、備忘録。

始まりはレプリケーションのエラーだった

本番稼動してなからとは言え、ミスが全くないというとすごーく自信ない。やはりエラーが出てしまうときはある。そういうときの対処法。

今回はレプリケーションでエラーを吐いて、レプリケーションが止まってしまった際にそれを解消する方法を書いてみる。
※ とは言え、本番で参照してなかったから、できる技であって、本番であったら、LVSから切り離すなりして、パケットが流れないようにしないといけない

まずは状態

基本的に、レプリケーションに関する情報(スレーブ)は、 mysql> show slave status\G で見ることができる。実際はこんな感じ。 (一部改変してます)

Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.0.1
Master_User: user
Master_Port: 999999
Connect_Retry: 60
Master_Log_File: master1-mysql-bin.000009
Read_Master_Log_Pos: 11111
Relay_Log_File: master-relay-bin.000016
Relay_Log_Pos: 22222
Relay_Master_Log_File: master-mysql-bin.000009
Slave_IO_Running: Yes
Slave_SQL_Running: No
Replicate_Do_DB:
Replicate_Do_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 1049
Last_Error: Error 'Unknown database 'test_2'' on query. Default database: 'test2'. Query: 'CREATE TABLE `test_2` (
`id` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin'
Skip_Counter: 0
Until_Condition: None
Until_Log_File:
Until_Log_Pos: 0
Master_SSL_Allowed: No
Master_SSL_CA_File:
Master_SSL_CA_Path:
Master_SSL_Cert:
Master_SSL_Cipher:
Master_SSL_Key:
Seconds_Behind_Master: NULL
Master_SSL_Verify_Server_Cert: No
Last_IO_Errno: 0
Last_IO_Error:
Last_SQL_Errno: 1049
Last_SQL_Error: Error 'Unknown database 'test'' on query. Default database: 'test'. Query: 'CREATE TABLE `test_2` (
`id` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin'
1 row in set (0.01 sec)

レプリケーション自体はどうかというと、

Slave_IO_Running: Yes Slave_SQL_Running: No

となっているので、Noがある限りエラーを吐いている…

んで、ここでdump流したりして、スレーブ作りなおせばいいんだけど、面倒なので、「エラーの吐いてる箇所を特定して、そこが飛ばしていい内容だったら飛ばしちゃえ」というお話し。

実際やってみる

実際は難しくなくて、単に以下のコマンドをroot権限と持つユーザで実行するだけ。

mysql> SET GLOBAL SQL_SLAVE_SKIP_COUNTER = 1;
mysql> START SLAVE;

SET文で、COUNTERを1にすることにより、今エラーを入ている箇所を飛ばすことができる。その上で、「START SLAVE」することで、エラーを飛ばしつつ、次のポジションのバイナリログを読むことができるようになる。

終わりに

基本的に、SET文とSTART文を繰り返せば、エラーの吐いたSQL文をいくらでも飛ばして、バイナリログを読むことができるようになる。
そういう意味ではスレーブの再構築とかは必要ないし、すごく負担が少ないと思う。

ただ、止まっている時間が数日とかになってしまうと、バイナリログを帯域に関係なく本気で読みに行ってしまうので、
一日とかもっと短い時間内に対処する範囲内で収まるときかなぁとも思う。

とりあえず、これで助かることもあるので、備忘録として残しておく。