2013年04月25日

innodb_support_xa と binlog の危ない関係

はてなブックマークに登録

昨日の記事 で innodb_support_xa = 0にすると RDS が速くなることを紹介したのですが、その後 Twitter で innodb_support_xa = 0 にするとクラッシュ時だけでなく通常時も binlog とトランザクションログの一貫性が無くなる(コミットする順序が前後する)可能性があることを指摘していただきました。

実際に、 MySQL 5.5 と 5.6 両方で、 innodb_support_xa の説明にそう書かれています。

もともと、 innodb_support_xa = 0 を設定した時は、
// innodb_support_xa = 1 のとき
1. prepare (sync)
2. lock
3. write binlog (sync if sync_binlog)
4. commit (sync)
5. unlock

// innodb_support_xa = 0 のとき
1. lock
2. write binlog (sync if sync_binlog)
3. commit (sync)
4. unlock
となることを期待していました。こうであれば、 fsync が prepare の分だけ減り、コミットに失敗しない限りは binlog とコミットログの一貫性は保たれるはずです。

しかし、 MySQL 5.5.31 のコードを見ると、実際には次のようになっていました。

// innodb_support_xa = 1 のとき
1. prepare (sync, lock)
2. write binlog (sync if sync_binlog)
3. commit (unlock, sync)

// innodb_support_xa = 0 のとき
1. prepare (do nothing)
2. write binlog (sync if sync_binlog)
3. commit (sync)
XA を OFF にすると、 prepare 時に取られていたロックが取られなくなり、複数スレッドで実行された時に binlog の書き込みとコミットの順序が守られなくなってしまいます。

しかし、 MySQL 5.5 と 5.6 では大幅に書きなおされているらしいです。

MySQL 5.6 のソースを呼んでみたところ、 sql/binlog.cc の MYSQL_BIN_LOG::ordered_commit() という関数で、 binlog とトランザクションの順序を守る処理が入っていました。 (というか、 5.5 で呼び出し側がそれを保証しないせいで innodb 側でロックをとると言う設計が無理矢理過ぎですよね)

この関数は binlog の書き込み、 sync, トランザクションのコミットまで順番に行う関数で、複数スレッドで実行されても大丈夫なようにしつつ、 binlog をグループコミットするような実装になっています。

1. トランザクションを flush キューに入れる
2. lock(log)
3. (stage1) flush キュー内の全てのトランザクションを binlog に書き、  sync キューに入れる
4. lock(sync) & unlock(log)
5. (stage2) sync キュー内の全てのトランザクションを取り出し、
    必要であれば最後のトランザクションまでのbinlogを sync してから、 commit キューに入れる
6. lock(commit) & unlock(sync)
7. (stage 3) commit キューの中身をすべて取り出し、順番にコミットしていく
8. unlock(commit)

この動作の解説とベンチマークが Binary Log Group Commit in MySQL 5.6 にありました。 XA を OFF にしてもトランザクションが失敗しない限りは binlog とトランザクションの一貫性が崩れないとはいえ、 innodb_support_xa=1 の時の性能が大幅に上がっているので、危険を冒してまで XA を OFF にする必要性は無さそうです。

この改善により、高負荷時は複数のトランザクションの binlog がまとめて書かれ sync されるのですが、残念なのは innodb 側のコミットは順番に1つずつ実行してしまうことです。 せっかく複数のトランザクションをコミット順に並べたのですから、トランザクションログへの書き込みも1回の fsync にまとめられたら性能が上がりそうです。

そういう改善がされていないのか検索してみたところ、 MySQL ではなくて MariaDB の方に見つけました。

[MDEV-232] Remove one fsync() inside engine's commit() method

該当のコミット

この MDEV-232 により、各 commit ではトランザクションログを書くだけで fsync せず、最後にまとめて fsync するようになります。 最近いくつかのLinuxディストリビューションで採用されたことで話題の MariaDB ですが、 AWS の Multi-AZ のように fsync が遅い環境では MySQL よりも高い性能を出せそうです。また機会があれば、 MySQL 5.6 と Maria DB のベンチマークを取ってみたいと思います。


@methane
songofacandy at 20:41│Comments(0)TrackBack(0)mysql 

トラックバックURL

この記事にコメントする

名前:
URL:
  情報を記憶: 評価: 顔   
 
 
 
Blog内検索
Archives
このブログについて
DSASとは、KLab が構築し運用しているコンテンツサービス用のLinuxベースのインフラです。現在5ヶ所のデータセンタにて構築し、運用していますが、我々はDSASをより使いやすく、より安全に、そしてより省力で運用できることを目指して、日々改良に勤しんでいます。
このブログでは、そんな DSAS で使っている技術の紹介や、実験してみた結果の報告、トラブルに巻き込まれた時の経験談など、広く深く、色々な話題を織りまぜて紹介していきたいと思います。
最新コメント