mysqlbinlog なんてやめて show binlog events を使おう

夜中に眠いのに master のディスク容量がいっぱいで、でも binlog 用のスペースがない slave たちはまだ明日起きてから対応するんでも間に合うとき。よくありますよね。

  • ほっといてエラーになるよりかはその場しのげる方がマシ
  • 多少サービス止めるなり i/o 負荷が高くてもいいから何とかしたい
  • 根本的には master 切り替えるしかない
  • だが、そこまで元気がない

そんなとき、いままで mysqlbinlog と格闘しながら pos のコピペに注意しつつやってたんですが、 show binlog events という便利コマンドがあることを今更知ったメモです。

基本的な方針

master から絶対に読み込みがないデータを消して、延命することにします。しかし innodb だったりするので単に delete しても容量は減りません。

そこで、えいやとアプリを止めて (or table lock して) 以下のようにすることにします。

CREATE TABLE hoge_history_new LIKE hoge_history;
INSERT INTO hoge_history_new SELECT * FROM hoge_history WHERE date >= '2012-01-01';
RENAME TABLE hoge_history TO hoge_history_bak, hoge_history_new TO hoge_history;
DROP TABLE hoge_histroy_bak;

ほんとに無停止でやりたいなら trigger で hoge_history_new に double write するとか。しかし trigger 仕込むのでミスするくらいなら、停止した上で停止時間短い方針考えるほうがいい時もあるかかも。

slave は drop してほしくない

これでなんとか master は 10 分停止くらいで drop したぶんの容量が稼げた。しかし slave はまだ容量に多少余裕あるので、できたらこんな drop して欲しくない。と言うよりデータ消しちゃだめじゃん!!

なので、データ消さない slave を 1 つ狙いを定め、上の sql を流す直前に master で show master status を実行し確認した pos まで replication を進めて止めておきます。

slave> STOP SLAVE;
slave> START SLAVE UNTIL master_log_file='hoge-bin.000123', master_log_pos=456789;

drop table だけ skip しよう

さて、次は drop table の直前まで replication を進めた上で stop slave し、 sql_slave_skip_counter=1 して drop だけ飛ばしてやりたい。

こんなとき、 pos をどこまで進めればいいかを今までは mysqlbinlog コマンドで調べていました。しかし以下の理由で、絶対もっといい方法あんだろ、と思っていた。

  • pos が境界の意味でどっちに含まれているのかよくわからん
  • skip_counter は event 単位で、 pos との対応が明確でない
  • だいたい出力多すぎで夜中にやると絶対ミスる

なんでドキュメントぷらぷら眺めてたら show binlog events という最強に見やすいコマンドがあったんですね!

つかいかた

まずは slave が何処まで replication しているか調べます。この例では pos=456789 の位置まで実行済み、ということになります。

slave> SHOW SLAVE STATUS\G
...
              Master_Log_File: hoge-bin.000123
...
          Exec_Master_Log_Pos: 456789

次に pos=456789 からのイベント内容を master で調べます。

master> SHOW BINLOG EVENTS IN 'hoge-bin.000123' FROM 456789 LIMIT 2; 
+-----------------+--------+------------+-----------+-------------+----------------------------------------+
| Log_name        | Pos    | Event_type | Server_id | End_log_pos | Info                                   |
+-----------------+--------+------------+-----------+-------------+----------------------------------------+
| hoge-bin.000123 | 456789 | Query      |       100 |      456901 | use `aho`; drop table hoge_history_bak | 
| hoge-bin.000123 | 456901 | Intvar     |       100 |      457005 | INSERT_ID=91187051                     | 
+-----------------+--------+------------+-----------+-------------+----------------------------------------+
2 rows in set (0.00 sec)

お、なんとも運がいいことにちょうど次のイベントが drop になってるじゃないですか! ということで slave で 1 イベントをスキップすれば ok。

slave> SET GLOBAL sql_slave_skip_counter=1
slave> START SLAVE;

これで、めでたく master だけデータが欠損した状態に出来ました。後はちゃんと寝てからこの slave に孫を作ってそのまま master に昇格させればよい。

pos と event

改めて考えてみれば、 pos は境界の場所であって、そこに対応する event があるわけじゃないんだよね。

1234567890123456789012345678901234567890
| event1 | event2       | event3       |
^pos=1   ^pos=10        ^pos=25        ^pos=40

たとえば pos=10 のときに 10 に対応する event はなく、前に event1 があって後ろに event2 がある、と。