2009年8月11日火曜日

マスターInnoDB、スレーブMyISAMが勧められない理由

 MySQLにおいて、マスターをInnoDBにして、スレーブをMyISAMにすると幸せになれるという主張をよく聞くことがあります。マスターは耐障害性の高いInnoDBにする一方で、スレーブは耐障害性が低くても大丈夫なので、InnoDBのかわりに高速とされるMyISAMを使えば、可用性と性能の両方をバランス良く実現できる、という考えです。
 しかし、多くの場合これで幸せになることはできません。マスターとスレーブでストレージエンジンを合わせた方が無難です。その理由を以下に示します。

●MyISAMはテーブルロックになる
 マスターへの更新結果はバイナリログに更新系SQL文として書かれ、スレーブのI/Oスレッドによってリレーログとして同じフォーマットで記録され、スレーブのSQLスレッドによってその更新系SQL文がそのまま実行されます。この更新系SQL文は、当然ながらスレーブに対して発行されるSELECT文と競合します(5.1のオプションである、行ベースのバイナリログでも同様)。MyISAMにはconcurrent insertという、INSERTと並行でSELECTを処理できる機能がありますが、テーブルが虫食い状態になっていると並行できないなどの制限もあります。またUPDATEとSELECTはばっちり競合してしまいます。
 集計系の処理のために数十分クラスのバッチ系のSELECT文を実行すると、多くの場合レプリケーションも数十分止まってしまいます。InnoDBは行ロックで、かつMVCCの恩恵でSELECTが更新系SQL文によってロックされないため、こうした問題を防げます。

●SQLスレッド停止時の対処が面倒になる
 InnoDBはトランザクション対応で、MyISAMは非対応です。トランザクションのACID特性の1つA特性である、ロールバックができるという機能をMyISAMは持っていません。InnoDBは持っています。この違いがあるため、レプリケーションにおいてSQLスレッドが止まったときにちょっと面倒なことになります。
 以下のようなSQL文をマスターで実行し、バイナリログに記録されたものとします。

1. START TRANSACTION;
2. INSERT
3. INSERT
4. INSERT
5. COMMIT;

 スレーブでも同じSQL文が実行されますが、ここで4でなんらかのエラー(一意制約違反など)が起きてスレーブ(SQLスレッド)が止まったとします。この場合、エラーを修正してSQLスレッドを再開することになりますが、このとき4からではなく、1から再スタートするのです。バイナリログはトランザクションの存在を認識していて、トランザクションの開始時点から再スタートするためです。しかしMyISAMはトランザクション対応ではないので、SQLスレッド停止前の時点ですでに2、3のINSERT結果が確定しています。1から再開すると、2、3はもう一度実行されるため、重複したINSERTが行なわれてしまうことになります。そのため、SET GLOBAL SQL_SLAVE_SKIP_COUNTERによって、4の前(あるいは後)まで明示的に飛ばしてあげる必要があります。

●スレーブ→マスターの昇格が難しくなる
 マスターを1台で運用している場合、マスターの障害時にはスレーブを昇格させる運用を取ることになります。しかしスレーブはMyISAMなので、InnoDBだった場合とは耐障害性や性能面で大きく傾向が変わる(遅くなる)可能性があります。MyISAMはテーブルロック、InnoDBは行ロックということで、多数のクライアントから並列で更新が行なわれるマスターでは、多くの場合InnoDBの方が良い性能が得られます。またInnoDBはクラスタ索引という特徴があるため、更新性能だけでなく、主キー検索についてもInnoDBの方が速くなる傾向にあります。InnoDBは検索性能も含めてすでに十分速いということも付け加えておきます。MySQL5.0以降はInnoDBのCPUスケーラビリティが大きく改善されていますし、MySQL5.4(beta)ではCPUスケーラビリティに加えてI/O性能も大きく改善されています。

 スレーブのストレージエンジンをマスターと分けるとしたら、これらのデメリットを差し置いてもメリットが上回る場合でしょう。例えばマスターをBlackholeにして、マスターの更新性能を上げるという形は良く取られます。またKickfireのようなDWH系に特化したストレージエンジンをスレーブに置いて、分析専用に使うことなども考えられます。

0 件のコメント:

コメントを投稿